Repository: Sage/carbon (ts)

Issue:
Form Inputs don't notify parent Tab if suddenly unmounted.
### Current behaviour

Conditionally rendered form components can cause issues with tabs and how they highlight errors.
So form fields will (via context) inform their parent tab if they have an error or not so it will highlight in red.
However, if that field is suddenly removed from the screen (because of conditional rendering), the tab is never notified that there's no longer an error to warn about.

### Expected behaviour

I would expect the field to notify the tab that and clear the error as validation no longer relevant.

### CodeSandbox or Storybook URL

https://codesandbox.io/s/tabs-dialogs-i1oo0l?file=/src/index.js

### JIRA Ticket (Sage Only)

_No response_

### Suggested Solution

I suspect you just need to add a cleanup function to the useEffect here:
https://github.com/Sage/carbon/blob/3238fb0cf79dab4ecd2d7364abf4cf8560055218/src/__internal__/form-field/form-field.component.tsx#L132
So that if a field is unmounted for any reason, it updates the error/warning/info status of the tab one final time?

### Carbon Version

110.0.3

### Design Tokens Version

_No response_

### What browsers are you seeing the problem on?

Chrome

### What Operating System are you seeing the problem on?

Windows

### Anything else we should know?

_No response_

### Confidentiality

- [X] I confirm there is no confidential or commercially sensitive information included.
Enhance Tabs so that they can summarise form input error on their page
### Desired behaviour

We had an ask to have the Tab component summarise validation messages from its children like you can see in the screenshot.

![image](https://user-images.githubusercontent.com/13569218/202203837-097b82a1-5b34-4e5a-91f8-c6d60527f553.png)


### Current behaviour

The current behaviour just allows you to provide a string that's independent of anything going on in the tab. 

### Suggested Solution

Since the Tabs component is already keeping a list of errors (as bools), simplest solution seems to be to change that to strings and then allow the user to pass a function instead of a string to format the list.

I've forked carbon and produced a working example as a POC (hacked around TS as not familiar with it) but it shows in principle, how this could be achieved in a fairly straight forward way.

https://github.com/Sage/carbon/commit/9776bf690ea62f00f09a4683492464e99864af35 


### CodeSandbox or Storybook URL

_No response_

### Anything else we should know?

Discussed with Carbon team members on Slack channel who suggested raising this here.

### Confidentiality

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

Pull request:
fix(tab): ensure that validation failures are no longer reported when children unmount FE-5527
fix #5591
fix #5618

### Proposed behaviour

<!--
A clear and concise description of what changes this PR makes.

If applicable, add screenshots of a codesandbox to help explain your request. You can paste these directly into GitHub.

Please DO NOT share screenshots or the source code of your project.

You can create a codesandbox to show the behaviour before/after this pull request by forking this template https://codesandbox.io/s/carbon-quickstart-j5pb2

If you include a CodeSandbox link, the bot will fork it with the new built version of carbon.
If you have a commit that includes fixes #XXXX and issue #XXXX has a CodeSandbox link in the body, the bot will fork
it with the new built version of carbon.
-->
Ensures that any validation failures reported to parent Tab components are removed when the
associated child unmounts.

![tabs validation fix](https://user-images.githubusercontent.com/44157880/205977195-ca3f1b84-a995-4681-992f-9a1c5f7f4f1e.gif)

Adds `showValidationsSummary` prop to `Tabs` to support the option of rendering a summary of any
validation string failures in the `ValidationIcon` `Tooltip`.

![image](https://user-images.githubusercontent.com/44157880/205975600-ecc647e7-da8f-4e09-a456-f571086eb471.png)

### Current behaviour

<!--
A clear and concise description of the behaviour before this change.

If applicable, add screenshots. You can paste these directly into GitHub.
-->
Validation failures are not removed when an input within a `Tab` unmounts
No support for rendering a summary of all validation string failures in the `ValidationIcon` `Tooltip`

### 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] All themes are supported if required
- [x] Unit tests added or updated if required
- [x] Cypress automation tests added or updated if required
- [x] Storybook added or updated if required
- [x] Typescript `d.ts` file added or updated if required
- [x] Related docs have been updated if required

#### QA

- [x] Tested in CodeSandbox/storybook
- [x] Add new Cypress test coverage if required
- [x] Carbon implementation matches Design System/designs
- [x] 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? -->
Use linked sandbox below for testing validation summary (linked issue has different implementation) and sandbox included in #5591

The following CodeSandbox is an example of the broken behaviour.
You can see the new behaviour by looking at the version in the comment by `codesandbox[bot]`.

<!-- Add CodeSandbox here -->
https://codesandbox.io/s/nostalgic-dawn-ubkcu0?file=/src/index.js

Patch:
diff --git a/src/__internal__/form-field/form-field.component.tsx b/src/__internal__/form-field/form-field.component.tsx
index f0fe7bcb9..0c9721d28 100644
--- a/src/__internal__/form-field/form-field.component.tsx
+++ b/src/__internal__/form-field/form-field.component.tsx
@@ -1,4 +1,10 @@
-import React, { useContext, useEffect, useMemo } from "react";
+import React, {
+  useContext,
+  useEffect,
+  useLayoutEffect,
+  useMemo,
+  useRef,
+} from "react";
 
 import { ValidationProps } from "__internal__/validations";
 import { MarginProps } from "styled-system";
@@ -126,10 +132,28 @@ const FormField = ({
     TabContext
   );
 
+  const isMounted = useRef(false);
+
+  useLayoutEffect(() => {
+    isMounted.current = true;
+
+    return () => {
+      isMounted.current = false;
+    };
+  }, []);
+
   useEffect(() => {
-    if (setError) setError(id, !!error);
-    if (setWarning) setWarning(id, !!warning);
-    if (setInfo) setInfo(id, !!info);
+    if (setError) setError(id, error);
+    if (setWarning) setWarning(id, warning);
+    if (setInfo) setInfo(id, info);
+
+    return () => {
+      if (!isMounted.current) {
+        if (setError && error) setError(id, false);
+        if (setWarning && warning) setWarning(id, false);
+        if (setInfo && info) setInfo(id, false);
+      }
+    };
   }, [id, setError, setWarning, setInfo, error, warning, info]);
 
   const marginProps = filterStyledSystemMarginProps(rest);
diff --git a/src/components/tabs/tab/tab.component.js b/src/components/tabs/tab/tab.component.js
index d6b56b883..3a5dfd394 100644
--- a/src/components/tabs/tab/tab.component.js
+++ b/src/components/tabs/tab/tab.component.js
@@ -31,27 +31,27 @@ const Tab = ({
   const [tabInfos, setTabInfos] = useState({});
 
   const setError = useCallback(
-    (childId, hasError) => {
-      if (tabErrors[childId] !== hasError) {
-        setTabErrors({ ...tabErrors, [childId]: hasError });
+    (childId, error) => {
+      if (tabErrors[childId] !== error) {
+        setTabErrors({ ...tabErrors, [childId]: error });
       }
     },
     [tabErrors]
   );
 
   const setWarning = useCallback(
-    (childId, hasWarning) => {
-      if (tabWarnings[childId] !== hasWarning) {
-        setTabWarnings({ ...tabWarnings, [childId]: hasWarning });
+    (childId, warning) => {
+      if (tabWarnings[childId] !== warning) {
+        setTabWarnings({ ...tabWarnings, [childId]: warning });
       }
     },
     [tabWarnings]
   );
 
   const setInfo = useCallback(
-    (childId, hasInfo) => {
-      if (tabInfos[childId] !== hasInfo) {
-        setTabInfos({ ...tabInfos, [childId]: hasInfo });
+    (childId, info) => {
+      if (tabInfos[childId] !== info) {
+        setTabInfos({ ...tabInfos, [childId]: info });
       }
     },
     [tabInfos]
diff --git a/src/components/tabs/tab/tab.d.ts b/src/components/tabs/tab/tab.d.ts
index afe2c0a65..9531a5716 100644
--- a/src/components/tabs/tab/tab.d.ts
+++ b/src/components/tabs/tab/tab.d.ts
@@ -2,9 +2,9 @@ import * as React from "react";
 import { PaddingProps } from "styled-system";
 
 export interface TabContextProps {
-  setError?: (childId: string, hasError: boolean) => void;
-  setWarning?: (childId: string, hasWarning: boolean) => void;
-  setInfo?: (childId: string, hasInfo: boolean) => void;
+  setError?: (childId: string, error?: boolean | string) => void;
+  setWarning?: (childId: string, warning?: boolean | string) => void;
+  setInfo?: (childId: string, info?: boolean | string) => void;
 }
 
 export interface TabProps extends PaddingProps {
diff --git a/src/components/tabs/tabs.component.js b/src/components/tabs/tabs.component.js
index 4e23e9c0c..b16317fdb 100644
--- a/src/components/tabs/tabs.component.js
+++ b/src/components/tabs/tabs.component.js
@@ -38,6 +38,7 @@ const Tabs = ({
   variant = "default",
   validationStatusOverride,
   headerWidth,
+  showValidationsSummary,
   ...rest
 }) => {
   /** The children nodes converted into an Array */
@@ -70,27 +71,27 @@ const Tabs = ({
   const [tabsInfos, setTabsInfos] = useState({});
 
   const updateErrors = useCallback(
-    (id, hasError) => {
-      if (tabsErrors[id] !== hasError) {
-        setTabsErrors({ ...tabsErrors, [id]: hasError });
+    (id, error) => {
+      if (tabsErrors[id] !== error) {
+        setTabsErrors({ ...tabsErrors, [id]: error });
       }
     },
     [tabsErrors]
   );
 
   const updateWarnings = useCallback(
-    (id, hasWarning) => {
-      if (tabsWarnings[id] !== hasWarning) {
-        setTabsWarnings({ ...tabsWarnings, [id]: hasWarning });
+    (id, warning) => {
+      if (tabsWarnings[id] !== warning) {
+        setTabsWarnings({ ...tabsWarnings, [id]: warning });
       }
     },
     [tabsWarnings]
   );
 
   const updateInfos = useCallback(
-    (id, hasInfo) => {
-      if (tabsInfos[id] !== hasInfo) {
-        setTabsInfos({ ...tabsInfos, [id]: hasInfo });
+    (id, info) => {
+      if (tabsInfos[id] !== info) {
+        setTabsInfos({ ...tabsInfos, [id]: info });
       }
     },
     [tabsInfos]
@@ -206,19 +207,16 @@ const Tabs = ({
         customLayout,
       } = child.props;
       const refId = `${tabId}-tab`;
+      const errors = tabsErrors[tabId];
+      const warnings = tabsWarnings[tabId];
+      const infos = tabsInfos[tabId];
 
-      const errors = tabsErrors[tabId]
-        ? Object.entries(tabsErrors[tabId]).filter((tab) => tab[1] === true)
-            .length
-        : 0;
-      const warnings = tabsWarnings[tabId]
-        ? Object.entries(tabsWarnings[tabId]).filter((tab) => tab[1] === true)
-            .length
-        : 0;
-      const infos = tabsInfos[tabId]
-        ? Object.entries(tabsInfos[tabId]).filter((tab) => tab[1] === true)
-            .length
-        : 0;
+      const errorsCount =
+        errors && Object.entries(errors).filter((tab) => tab[1]).length;
+      const warningsCount =
+        warnings && Object.entries(warnings).filter((tab) => tab[1]).length;
+      const infosCount =
+        infos && Object.entries(infos).filter((tab) => tab[1]).length;
 
       const hasOverride =
         validationStatusOverride && validationStatusOverride[tabId];
@@ -228,15 +226,31 @@ const Tabs = ({
         hasOverride && validationStatusOverride[tabId].warning;
       const infoOverride = hasOverride && validationStatusOverride[tabId].info;
       const tabHasError =
-        errorOverride !== undefined ? errorOverride : errors > 0;
+        errorOverride !== undefined ? errorOverride : !!errorsCount;
       const tabHasWarning =
         warningOverride !== undefined
           ? warningOverride
-          : warnings > 0 && !tabHasError;
+          : !!warningsCount && !tabHasError;
       const tabHasInfo =
         infoOverride !== undefined
           ? infoOverride
-          : infos > 0 && !tabHasError && !tabHasWarning;
+          : !!infosCount && !tabHasError && !tabHasWarning;
+
+      const getValidationMessage = (message, validations = {}) => {
+        const summaryOfMessages = Object.values(validations).filter(
+          (value) => value && typeof value === "string"
+        );
+
+        if (!showValidationsSummary || !summaryOfMessages.length) {
+          return message;
+        }
+
+        if (summaryOfMessages.length === 1) {
+          return summaryOfMessages[0];
+        }
+
+        return summaryOfMessages.map((value) => `• ${value}`).join("\n");
+      };
 
       const tabTitle = (
         <TabTitle
@@ -259,9 +273,9 @@ const Tabs = ({
           borders={borders !== "off"}
           siblings={siblings}
           titlePosition={titlePosition}
-          errorMessage={errorMessage}
-          warningMessage={warningMessage}
-          infoMessage={infoMessage}
+          errorMessage={getValidationMessage(errorMessage, errors)}
+          warningMessage={getValidationMessage(warningMessage, warnings)}
+          infoMessage={getValidationMessage(infoMessage, infos)}
           alternateStyling={variant === "alternate"}
           noLeftBorder={["no left side", "no sides"].includes(borders)}
           noRightBorder={["no right side", "no sides"].includes(borders)}
@@ -408,6 +422,10 @@ Tabs.propTypes = {
       info: PropTypes.bool,
     }),
   }),
+  /** When this prop is set any string validation failures in the children of each Tab
+   * will be summaraised in the Tooltip next to the Tab title
+   */
+  showValidationsSummary: PropTypes.bool,
 };
 
 export { Tabs, Tab };
diff --git a/src/components/tabs/tabs.d.ts b/src/components/tabs/tabs.d.ts
index 88ecac486..590e4ec4b 100644
--- a/src/components/tabs/tabs.d.ts
+++ b/src/components/tabs/tabs.d.ts
@@ -40,6 +40,10 @@ export interface TabsProps extends MarginProps {
       info?: boolean;
     };
   };
+  /** When this prop is set any string validation failures in the children of each Tab
+   * will be summaraised in the Tooltip next to the Tab title
+   */
+  showValidationsSummary?: boolean;
 }
 
 declare function Tabs(props: TabsProps): JSX.Element;
diff --git a/src/components/tabs/tabs.spec.js b/src/components/tabs/tabs.spec.js
index 1e5b661c5..80427965f 100644
--- a/src/components/tabs/tabs.spec.js
+++ b/src/components/tabs/tabs.spec.js
@@ -17,6 +17,8 @@ import {
 } from "../../__spec_helper__/test-utils";
 import { StyledTabsHeaderWrapper } from "./__internal__/tabs-header/tabs-header.style";
 import { DrawerSidebarContext } from "../drawer";
+import Textbox from "../textbox";
+import ValidationIcon from "../../__internal__/validations";
 
 function render(props, mountOptions) {
   return mount(
@@ -931,4 +933,155 @@ describe("Tabs", () => {
       });
     });
   });
+
+  describe("when children of Tab have validation failures", () => {
+    const MockComponent = ({ show = true, error, warning, info }) => (
+      <Tabs data-element="bar" data-role="baz">
+        <Tab
+          tabId="1"
+          title="Test"
+          errorMessage=""
+          warningMessage=""
+          infoMessage=""
+        >
+          {show && (
+            <Textbox
+              value="foo"
+              onChange={() => {}}
+              error={error}
+              warning={warning}
+              info={info}
+            />
+          )}
+        </Tab>
+      </Tabs>
+    );
+
+    it.each(["error", "warning", "info"])(
+      "any %s failure in a child component is correctly reported in the TabTitle",
+      (validation) => {
+        const validationProp = { [validation]: true };
+        const wrapper = mount(<MockComponent {...validationProp} />);
+
+        expect(wrapper.find(ValidationIcon).exists()).toBe(true);
+
+        wrapper.setProps({ show: false });
+        wrapper.update();
+        expect(wrapper.update().find(ValidationIcon).exists()).toBe(false);
+      }
+    );
+  });
+
+  describe.each(["error", "warning", "info"])(
+    "showValidationsSummary",
+    (validation) => {
+      it(`passes the ${validation} validation failures from the child inputs to the Tab's title when they are strings`, () => {
+        const message = mount(
+          <Tabs data-element="bar" data-role="baz" showValidationsSummary>
+            <Tab tabId="1" title="Test">
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: validation }}
+              />
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: validation }}
+              />
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: validation }}
+              />
+            </Tab>
+          </Tabs>
+        )
+          .find(TabTitle)
+          .prop(`${validation}Message`);
+
+        expect(message).toEqual(
+          `• ${validation}\n• ${validation}\n• ${validation}`
+        );
+      });
+
+      it(`does not pass the ${validation} validation failures from the child inputs to the Tab's title when they are not strings`, () => {
+        const message = mount(
+          <Tabs data-element="bar" data-role="baz" showValidationsSummary>
+            <Tab tabId="1" title="Test">
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: validation }}
+              />
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: validation }}
+              />
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: true }}
+              />
+            </Tab>
+          </Tabs>
+        )
+          .find(TabTitle)
+          .prop(`${validation}Message`);
+
+        expect(message).toEqual(`• ${validation}\n• ${validation}`);
+      });
+
+      it(`does not add a "•" when there is only one string ${validation} validation failure`, () => {
+        const message = mount(
+          <Tabs data-element="bar" data-role="baz" showValidationsSummary>
+            <Tab tabId="1" title="Test">
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: validation }}
+              />
+            </Tab>
+          </Tabs>
+        )
+          .find(TabTitle)
+          .prop(`${validation}Message`);
+
+        expect(message).toEqual(validation);
+      });
+
+      it(`passes the ${validation}Message if there is only boolean ${validation} validation failures`, () => {
+        const message = mount(
+          <Tabs data-element="bar" data-role="baz" showValidationsSummary>
+            <Tab
+              tabId="1"
+              title="Test"
+              {...{ [`${validation}Message`]: `${validation} message` }}
+            >
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: true }}
+              />
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: true }}
+              />
+              <Textbox
+                value="foo"
+                onChange={() => {}}
+                {...{ [validation]: true }}
+              />
+            </Tab>
+          </Tabs>
+        )
+          .find(TabTitle)
+          .prop(`${validation}Message`);
+
+        expect(message).toEqual(`${validation} message`);
+      });
+    }
+  );
 });
diff --git a/src/components/tabs/tabs.stories.mdx b/src/components/tabs/tabs.stories.mdx
index 0e99e4fb6..c649f2d53 100644
--- a/src/components/tabs/tabs.stories.mdx
+++ b/src/components/tabs/tabs.stories.mdx
@@ -1761,6 +1761,92 @@ CSS string.
   </Story>
 </Canvas>
 
+### With string validations summarised
+
+By default the strings passed to `errorMessage`, `warningMessage` and `infoMessage` props are what is 
+used in the validation tooltip. However, setting the `showValidationsSumary` prop allows for the 
+summarising of validation messages from the children of a given `Tab`. You can see an example of this 
+by hovering your mouse pointer over the `ValidationIcon`s in the example below.
+
+<Canvas>
+  <Story name="with string validations summarised" parameters={{ chromatic: { disable: true } }}>
+    {() => {
+      const [errors, setErrors] = useState({
+        one: 'This is an error',
+        two: 'Here is another error'
+      });
+      const [warnings, setWarnings] = useState({
+        one: 'This is a warning',
+        two: 'Here is another warning'
+      });
+      const [infos, setInfos] = useState({ 
+        one: 'This is an info', 
+        two: 'Here is another info'
+      });
+      return (
+        <div style={{ padding: "4px" }}>
+          <Tabs align="left" position="top" showValidationsSummary>
+            <Tab
+              tabId="tab-1"
+              title="Tab 1"
+              key="tab-1"
+            >
+              <Checkbox
+                label="Error 1"
+                error={errors.one}
+                onChange={e => e.target.checked ? setErrors({ ...errors, one: 'This is an error' }) : setErrors({ ...errors, one: '' })}
+                checked={errors.one}
+              />
+              <Checkbox
+                label="Error 2"
+                error={errors.two}
+                onChange={e => e.target.checked ? setErrors({ ...errors, two: 'Here is another error' }) : setErrors({ ...errors, two: '' })}
+                checked={errors.two}
+              />
+            </Tab>
+            <Tab
+              tabId="tab-2"
+              title="Tab 2"
+              key="tab-2"
+            >
+              <Checkbox
+                label="Warning 1"
+                warning={warnings.one}
+                onChange={e => e.target.checked ? setWarnings({ ...warnings, one: 'This is a warning' }) : setWarnings({ ...warnings, one: '' })}
+                checked={warnings.one}
+              />
+              <Checkbox
+                label="Warning 2"
+                warning={warnings.two}
+                onChange={e => e.target.checked ? setWarnings({ ...warnings, two: 'Here is another warning' }) : setWarnings({ ...warnings, two: '' })}
+                checked={warnings.two}
+              />
+            </Tab>
+            <Tab
+              tabId="tab-3"
+              title="Tab 3"
+              key="tab-3"
+            >
+              <Checkbox
+                label="Info 1"
+                info={infos.one}
+                onChange={e => e.target.checked ? setInfos({ ...infos, one: true }) : setInfos({ ...infos, one: '' })}
+                checked={infos.one}
+              />
+              <Checkbox
+                label="Info 2"
+                info={infos.two}
+                onChange={e => e.target.checked ? setInfos({ ...infos, two: 'This is a warning' }) : setInfos({ ...infos, two: '' })}
+                checked={infos.two}
+              />
+            </Tab>
+          </Tabs>
+        </div>
+      );
+    }}
+  </Story>
+</Canvas>
+
 ### Integrating with an external history
 
 It is possible to integrate the `Tabs` component with an external `history` to manipulate the location when a given `Tab`


Test patch:
diff --git a/src/components/tabs/tabs.test.js b/src/components/tabs/tabs.test.js
index eec75f04c..9602ddac7 100644
--- a/src/components/tabs/tabs.test.js
+++ b/src/components/tabs/tabs.test.js
@@ -186,6 +186,72 @@ const TabsComponentValidations = ({ ...props }) => {
   );
 };
 
+// eslint-disable-next-line react/prop-types
+const TabsComponentWithValidationsSummary = ({ validation }) => {
+  return (
+    <div
+      style={{
+        padding: "4px",
+      }}
+    >
+      <Tabs align="left" position="top" showValidationsSummary>
+        <Tab
+          errorMessage="error"
+          warningMessage="warning"
+          infoMessage="info"
+          tabId="tab-1"
+          title="Tab 1"
+          key="tab-1"
+        >
+          <Checkbox label="foo" {...validation} />
+          <Checkbox label="foo" {...validation} />
+          <Checkbox label="foo" {...validation} />
+        </Tab>
+      </Tabs>
+    </div>
+  );
+};
+
+// eslint-disable-next-line react/prop-types
+const TabsComponentValidationsUnregistering = ({ validation }) => {
+  const [show, setShow] = React.useState(true);
+
+  return (
+    <div
+      style={{
+        padding: "4px",
+      }}
+    >
+      <button
+        data-element="foo-button"
+        type="button"
+        onClick={() => setShow(false)}
+      >
+        Hide Tab Child
+      </button>
+      <Tabs align="left" position="top">
+        <Tab
+          errorMessage="error"
+          warningMessage="warning"
+          infoMessage="info"
+          tabId="tab-1"
+          title="Tab 1"
+          key="tab-1"
+        >
+          {show && (
+            <Checkbox
+              label="Add error"
+              onChange={() => {}}
+              checked
+              {...validation}
+            />
+          )}
+        </Tab>
+      </Tabs>
+    </div>
+  );
+};
+
 const TabsValidationOverride = () => {
   const [validation, setValidation] = React.useState({
     error: true,
@@ -555,6 +621,41 @@ context("Testing Tabs component", () => {
       }
     );
 
+    it.each(["error", "warning", "info"])(
+      "should verify when the ValidationIcon is hovered over that a summary of the %s messages is displayed",
+      (validationMessage) => {
+        const validation = { [validationMessage]: validationMessage };
+
+        CypressMountWithProviders(
+          <TabsComponentWithValidationsSummary validation={validation} />
+        );
+
+        tabById(1)
+          .trigger("mouseover")
+          .then(() => {
+            tooltipPreview().should(
+              "have.text",
+              `• ${validationMessage}\n• ${validationMessage}\n• ${validationMessage}`
+            );
+          });
+      }
+    );
+
+    it.each(["error", "warning", "info"])(
+      "should no longer report the any validation failures of children no longer mounted",
+      (type) => {
+        const validation = { [type]: true };
+
+        CypressMountWithProviders(
+          <TabsComponentValidationsUnregistering validation={validation} />
+        );
+
+        getDataElementByValue("foo-button").click();
+
+        tabById(1).children().children().should("not.exist");
+      }
+    );
+
     it("should verify Tabs validation status is overridden", () => {
       CypressMountWithProviders(<TabsValidationOverride />);