Repository: Sage/carbon (ts)

Issue:
Pager component retains focus styling highlight on pagination buttons
### Description

![Screenshot 2024-04-18 at 16 25 23](https://github.com/Sage/carbon/assets/53473905/8aeed1f2-6ce0-48ad-9573-82cd0fbb7b65)

going from "next" to "previous" then back to "next" will replicate.
the focus from the previous render of the button will retain the black focus bar under the text

### Reproduction

https://carbon.sage.com/?path=/docs/pager--docs

### Steps to reproduce

going from "next" to "previous" then back to "next" will replicate.
the focus from the previous render of the button will retain the black focus bar under the text

There is a recording attached to the JIRA ticket for further detail calrification

### JIRA ticket numbers (Sage only)

SBS-93920

### Suggested solution

_No response_

### Carbon version

130.0.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.
Bad style on current crumb when navigating insde
### Description

I am navigating in the breadcrumb, i expect to not have the big black bold underligne below my current page.
If i navigate a lot, i can have all my crumbs underligned with bold black underlignes
![image](https://github.com/Sage/carbon/assets/41692955/39658a9e-acf2-47b4-8bb3-877d4d627083)

For me case, i can do a workaround to refresh my whole component to clean the style (using a key on my component)

### Reproduction

https://stackblitz.com/edit/parsium-carbon-starter-angxru?file=src%2FApp.tsx

### Steps to reproduce

Click on Path 2 for exemple
Click on Add crumb 


### JIRA ticket numbers (Sage only)

_No response_

### Suggested solution

_No response_

### Carbon version

140.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(link): ensure focus styles are removed when underlying element loses focus
fix #6808
fix #6688

### 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.
-->
Ensures that styling updates when focus is lost but no blur is not triggered. This means that when a `Crumb` is clicked and becomes current the focus styling is not persisted.

![2024-07-12 18 19 08](https://github.com/user-attachments/assets/190c3d72-9fea-486f-906e-7ddbed3d34d3)

### 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.
-->
Part of the Link focus styling is persisted when the link loses focus but no blur event isn't triggered. See linked issue for current behaviour

### Checklist

<!-- Each PR should include the following -->

- [x] Commits follow our style guide
- [x] Related issues linked in commit messages if required
- [x] Screenshots are included in the PR if useful
- [x] Playwright automation tests added or updated if required
- [x] Storybook added or updated if required

#### QA

- [ ] 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>
-->
Test story added for `Breadcrumb` component

Patch:
diff --git a/src/components/breadcrumbs/breadcrumbs.pw.tsx b/src/components/breadcrumbs/breadcrumbs.pw.tsx
index 72b4df374..d210d14be 100644
--- a/src/components/breadcrumbs/breadcrumbs.pw.tsx
+++ b/src/components/breadcrumbs/breadcrumbs.pw.tsx
@@ -5,7 +5,11 @@ import {
   allCrumbs,
   crumbAtIndex,
 } from "../../../playwright/components/breadcrumbs/index";
-import { Default, DefaultCrumb } from "./components.test-pw";
+import {
+  Default,
+  DefaultCrumb,
+  FocusedCrumbBecomesCurrent,
+} from "./components.test-pw";
 import {
   checkAccessibility,
   expectEventWasNotCalled,
@@ -45,6 +49,126 @@ test.describe("should render Breadcrumbs component", async () => {
     await expect(crumbAtIndex(page, 2).locator("a")).toBeFocused();
   });
 
+  test("should have correct focus styling when a crumb is focused but not current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent />);
+
+    await crumbAtIndex(page, 0).locator("button").focus();
+    await expect(crumbAtIndex(page, 0).locator("button")).toHaveCSS(
+      "background-color",
+      "rgb(255, 218, 128)"
+    );
+    await expect(crumbAtIndex(page, 0).locator("span").first()).toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should have correct focus styling when a crumb with href set is focused but not current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent hasHref />);
+
+    await crumbAtIndex(page, 0).locator("a").focus();
+    await expect(crumbAtIndex(page, 0).locator("a")).toHaveCSS(
+      "background-color",
+      "rgb(255, 218, 128)"
+    );
+    await expect(crumbAtIndex(page, 0).locator("span").first()).toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should not have any focus styling when a crumb is clicked and becomes current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent />);
+
+    await crumbAtIndex(page, 0).locator("button").click();
+    await expect(crumbAtIndex(page, 0).locator("span").first()).not.toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should not have any focus styling when a crumb with href set is clicked and becomes current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent hasHref />);
+
+    await crumbAtIndex(page, 0).locator("a").click();
+    await expect(crumbAtIndex(page, 0).locator("span").first()).not.toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should not have any focus styling when user presses Enter key on focused crumb and it becomes current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent />);
+
+    await crumbAtIndex(page, 0).locator("button").focus();
+    await crumbAtIndex(page, 0).locator("button").press("Enter");
+    await expect(crumbAtIndex(page, 0).locator("span").first()).not.toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should not have any focus styling when user presses Enter key on focused crumb with href set and it becomes current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent hasHref />);
+
+    await crumbAtIndex(page, 0).locator("a").focus();
+    await crumbAtIndex(page, 0).locator("a").press("Enter");
+    await expect(crumbAtIndex(page, 0).locator("span").first()).not.toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should not have any focus styling when user presses Space key on focused crumb and it becomes current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent />);
+
+    await crumbAtIndex(page, 0).locator("button").focus();
+    await crumbAtIndex(page, 0).locator("button").press("Space");
+    await expect(crumbAtIndex(page, 0).locator("span").first()).not.toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
+  test("should have focus styling when user presses Space key on focused crumb with href set and it becomes current", async ({
+    mount,
+    page,
+  }) => {
+    await mount(<FocusedCrumbBecomesCurrent hasHref />);
+
+    await crumbAtIndex(page, 0).locator("a").focus();
+    await crumbAtIndex(page, 0).locator("a").press("Space");
+    await expect(crumbAtIndex(page, 0).locator("a")).toHaveCSS(
+      "background-color",
+      "rgb(255, 218, 128)"
+    );
+    await expect(crumbAtIndex(page, 0).locator("span").first()).toHaveCSS(
+      "box-shadow",
+      "rgba(0, 0, 0, 0.9) 0px 4px 0px 0px"
+    );
+  });
+
   [
     {
       isCurrent: true,
diff --git a/src/components/link/link.component.tsx b/src/components/link/link.component.tsx
index 4e0984489..e3af5bb68 100644
--- a/src/components/link/link.component.tsx
+++ b/src/components/link/link.component.tsx
@@ -1,4 +1,4 @@
-import React, { useContext, useMemo, useState } from "react";
+import React, { useContext, useEffect, useMemo, useState } from "react";
 
 import Icon, { IconType } from "../icon";
 import MenuContext from "../menu/__internal__/menu.context";
@@ -162,6 +162,12 @@ export const Link = React.forwardRef<
       );
     };
 
+    useEffect(() => {
+      if (disabled || !(href || onClick)) {
+        setHasFocus(false);
+      }
+    }, [disabled, href, onClick]);
+
     return (
       <StyledLink
         isSkipLink={isSkipLink}


Test patch:
diff --git a/src/components/breadcrumbs/breadcrumbs-test.stories.tsx b/src/components/breadcrumbs/breadcrumbs-test.stories.tsx
index e259bf430..8e58b012b 100644
--- a/src/components/breadcrumbs/breadcrumbs-test.stories.tsx
+++ b/src/components/breadcrumbs/breadcrumbs-test.stories.tsx
@@ -4,7 +4,7 @@ import { Crumb, CrumbProps } from "./crumb";
 
 export default {
   title: "Breadcrumbs/Test",
-  includeStories: ["DefaultCrumb"],
+  includeStories: ["DefaultCrumb", "WhenFocusedCrumbBecomesCurrent"],
   parameters: {
     info: { disable: true },
     chromatic: {
@@ -48,5 +48,22 @@ export const DefaultCrumb = (props: Partial<CrumbProps>) => {
   );
 };
 
+export const WhenFocusedCrumbBecomesCurrent = () => {
+  const [current, setCurrent] = React.useState(false);
+
+  return (
+    <>
+      <Breadcrumbs>
+        <Crumb href="#bar" onClick={() => setCurrent(true)} isCurrent={current}>
+          Crumb{current ? "" : " not"} current
+        </Crumb>
+      </Breadcrumbs>
+
+      <div id="bar">Container</div>
+    </>
+  );
+};
+
 Default.storyName = "default";
 DefaultCrumb.storyName = "single crumb";
+WhenFocusedCrumbBecomesCurrent.storyName = "when focused crumb becomes current";
diff --git a/src/components/breadcrumbs/components.test-pw.tsx b/src/components/breadcrumbs/components.test-pw.tsx
index cc794d63d..6d78925c4 100644
--- a/src/components/breadcrumbs/components.test-pw.tsx
+++ b/src/components/breadcrumbs/components.test-pw.tsx
@@ -25,4 +25,23 @@ const DefaultCrumb = (props: Partial<CrumbProps>) => {
   );
 };
 
-export { Default, DefaultCrumb };
+const FocusedCrumbBecomesCurrent = ({ hasHref = false }) => {
+  const [current, setCurrent] = React.useState(false);
+
+  return (
+    <>
+      <Breadcrumbs>
+        <Crumb
+          href={hasHref ? "#foo" : undefined}
+          onClick={() => setCurrent(true)}
+          isCurrent={current}
+        >
+          foo
+        </Crumb>
+      </Breadcrumbs>
+      {hasHref && <div id="foo">foo</div>}
+    </>
+  );
+};
+
+export { Default, DefaultCrumb, FocusedCrumbBecomesCurrent };