Repository: RocketChat/Rocket.Chat
Pull request #35932: test: Adopt jest testing in `ui-composer`
Author: login: dougfabris
Labels: name: stat: ready to merge
description: PR tested and approved waiting for merge
name: stat: QA assured
description: Means it has been tested and approved by a company insider
Created: 2025-05-06T15:42:06Z | Merged: 2025-05-07T20:48:02Z

<!-- This is a pull request template, you do not need to uncomment or remove the comments, they won't show up in the PR text. -->

<!-- Your Pull Request name should start with one of the following tags
  feat: Adding a new feature
  refactor: A code change that doesn't change behavior (it doesn't add anything and doesn't fix anything)
  fix: For bug fixes that affect the end-user
  chore: For small tasks
  docs: For documentation
  ci: For updating CI configuration
  test: For adding tests
  i18n: For updating any translations
  regression: Issues created/reported/fixed during the development phase. kind of problem that never existed in production and that we don't need to list in a changelog for the end user
-->

<!-- Checklist!!! If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code. 
  - I have read the Contributing Guide - https://github.com/RocketChat/Rocket.Chat/blob/develop/.github/CONTRIBUTING.md#contributing-to-rocketchat doc
  - I have signed the CLA - https://cla-assistant.io/RocketChat/Rocket.Chat
  - Lint and unit tests pass locally with my changes
  - I have added tests that prove my fix is effective or that my feature works (if applicable)
  - I have added necessary documentation (if applicable)
  - Any dependent changes have been merged and published in downstream modules
-->

## Proposed changes (including videos or screenshots)
<!--
  Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request.
  If it fixes a bug or resolves a feature request, be sure to link to that issue below.
  This description won't be displayed to our end users in the release notes, so feel free to add as much technical context as needed.
  If the changes introduced in this pull request must be presented in the release notes, make sure to add a changeset file. Check our guidelines for adding a changeset to your pull request: https://developer.rocket.chat/contribute-to-rocket.chat/modes-of-contribution/participate-in-rocket.chat-development/development-workflow#4.-adding-changeset-to-your-pull-request 
-->

## Issue(s)
<!-- Link the issues being closed by or related to this PR. For example, you can use #594 if this PR closes issue number 594 -->

## Steps to test or reproduce
<!-- Mention how you would reproduce the bug if not mentioned on the issue page already. Also mention which screens are going to have the changes if applicable -->

## Further comments
<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc... -->

[CORE-1137]

---

This pull request refactors the `MessageComposerInput` component in the RocketChat/Rocket.Chat repository by using margin instead of padding. It introduces several updates and additions to the `ui-composer` package:

1. **Storybook Configuration**: An accessibility addon is added to the Storybook configuration to enhance component accessibility testing.

2. **Jest Configuration**: A new Jest configuration file is added, including preset and module mappings to facilitate testing.

3. **Testing Enhancements**:
   - New test cases are introduced for the `MessageComposer`, `MessageComposerHint`, and `MessageFooterCallout` components, focusing on rendering and accessibility checks.
   - Jest snapshot tests are added for `MessageComposer`, `MessageComposerHint`, and `MessageFooterCallout` components to ensure UI consistency and detect unintended rendering changes.

4. **Story Title Updates**:
   - The story title path for the `MessageComposerHint` component is updated.
   - The story title for `MessageFooterCallout` is changed from 'Components/MessageComposer/Locked' to 'Components/MessageComposerCallout' to better reflect the component's purpose.

5. **Type Definition and Property Adjustment**: The `MessageComposerInput.tsx` file is updated to simplify type definitions and adjust the padding property.

These changes aim to improve the maintainability and accessibility of the `ui-composer` components while ensuring consistent UI behavior through comprehensive testing.

[CORE-1137]: https://rocketchat.atlassian.net/browse/CORE-1137?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/packages/ui-composer/.storybook/main.ts b/packages/ui-composer/.storybook/main.ts
index 8364908884e3d..561cd8e5766c5 100644
--- a/packages/ui-composer/.storybook/main.ts
+++ b/packages/ui-composer/.storybook/main.ts
@@ -4,7 +4,12 @@ import type { StorybookConfig } from '@storybook/react-webpack5';
 
 const config: StorybookConfig = {
 	stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
-	addons: [getAbsolutePath('@storybook/addon-essentials'), './webpackAddon', getAbsolutePath('@storybook/addon-styling-webpack')],
+	addons: [
+		getAbsolutePath('@storybook/addon-a11y'),
+		getAbsolutePath('@storybook/addon-essentials'),
+		'./webpackAddon',
+		getAbsolutePath('@storybook/addon-styling-webpack'),
+	],
 
 	framework: {
 		name: getAbsolutePath('@storybook/react-webpack5'),
diff --git a/packages/ui-composer/jest.config.ts b/packages/ui-composer/jest.config.ts
new file mode 100644
index 0000000000000..1849c7f047ef0
--- /dev/null
+++ b/packages/ui-composer/jest.config.ts
@@ -0,0 +1,11 @@
+import client from '@rocket.chat/jest-presets/client';
+import type { Config } from 'jest';
+
+export default {
+	preset: client.preset,
+	setupFilesAfterEnv: [...client.setupFilesAfterEnv],
+	moduleNameMapper: {
+		'^react($|/.+)': '<rootDir>/../../node_modules/react$1',
+		'^react-dom($|/.+)': '<rootDir>/../../node_modules/react-dom$1',
+	},
+} satisfies Config;
diff --git a/packages/ui-composer/package.json b/packages/ui-composer/package.json
index 69d178384750e..ea2e43f23c835 100644
--- a/packages/ui-composer/package.json
+++ b/packages/ui-composer/package.json
@@ -15,6 +15,8 @@
 		"lint": "eslint --ext .js,.jsx,.ts,.tsx .",
 		"lint:fix": "eslint --ext .js,.jsx,.ts,.tsx . --fix",
 		"storybook": "storybook dev -p 6006",
+		"test": "jest",
+		"testunit": "jest",
 		"typecheck": "tsc --noEmit"
 	},
 	"devDependencies": {
@@ -23,6 +25,8 @@
 		"@rocket.chat/eslint-config": "workspace:^",
 		"@rocket.chat/fuselage": "~0.62.0",
 		"@rocket.chat/icons": "^0.42.0",
+		"@rocket.chat/jest-presets": "workspace:~",
+		"@storybook/addon-a11y": "^8.6.4",
 		"@storybook/addon-actions": "^8.6.4",
 		"@storybook/addon-docs": "^8.6.4",
 		"@storybook/addon-essentials": "^8.6.4",
@@ -30,12 +34,14 @@
 		"@storybook/addon-webpack5-compiler-babel": "^3.0.5",
 		"@storybook/react": "^8.6.4",
 		"@storybook/react-webpack5": "^8.6.4",
+		"@types/jest": "~29.5.14",
 		"@types/react": "~18.3.17",
 		"@types/react-dom": "~18.3.5",
 		"eslint": "~8.45.0",
 		"eslint-plugin-react": "~7.37.2",
 		"eslint-plugin-react-hooks": "~5.0.0",
 		"eslint-plugin-storybook": "^0.11.4",
+		"jest": "~29.7.0",
 		"react": "~18.3.1",
 		"react-dom": "~18.3.1",
 		"storybook": "^8.6.4",
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposer.spec.tsx b/packages/ui-composer/src/MessageComposer/MessageComposer.spec.tsx
new file mode 100644
index 0000000000000..32de84a9685cd
--- /dev/null
+++ b/packages/ui-composer/src/MessageComposer/MessageComposer.spec.tsx
@@ -0,0 +1,19 @@
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import * as stories from './MessageComposer.stories';
+
+const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
+
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+	const tree = render(<Story />);
+	expect(tree.baseElement).toMatchSnapshot();
+});
+
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+	const { container } = render(<Story />);
+
+	const results = await axe(container);
+	expect(results).toHaveNoViolations();
+});
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx b/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx
index e777a66553a61..b15ddb1a3ea0f 100644
--- a/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx
+++ b/packages/ui-composer/src/MessageComposer/MessageComposer.stories.tsx
@@ -21,28 +21,25 @@ export default {
 
 const MessageToolbarActions = () => (
 	<MessageComposerToolbarActions>
-		<MessageComposerAction icon='emoji' />
+		<MessageComposerAction title='emoji' icon='emoji' />
 		<MessageComposerActionsDivider />
-		<MessageComposerAction icon='bold' />
-		<MessageComposerAction icon='italic' />
-		<MessageComposerAction icon='underline' />
-		<MessageComposerAction icon='strike' />
-		<MessageComposerAction icon='code' />
-		<MessageComposerAction icon='multiline' />
-		<MessageComposerAction icon='link' />
-		<MessageComposerAction icon='katex' />
-		<MessageComposerAction icon='arrow-return' />
+		<MessageComposerAction title='bold' icon='bold' />
+		<MessageComposerAction title='italic' icon='italic' />
+		<MessageComposerAction title='underline' icon='underline' />
+		<MessageComposerAction title='strike' icon='strike' />
+		<MessageComposerAction title='code' icon='code' />
+		<MessageComposerAction title='multiline' icon='multiline' />
+		<MessageComposerAction title='link' icon='link' />
+		<MessageComposerAction title='katex' icon='katex' />
 		<MessageComposerActionsDivider />
-		<MessageComposerAction icon='mic' />
-		<MessageComposerAction icon='video' />
-		<MessageComposerAction icon='clip' />
-		<MessageComposerAction icon='plus' />
+		<MessageComposerAction title='mic' icon='mic' />
+		<MessageComposerAction title='video' icon='video' />
+		<MessageComposerAction title='attachment' icon='clip' />
+		<MessageComposerAction title='more' icon='plus' />
 	</MessageComposerToolbarActions>
 );
 
-export const MessageToolberActions: StoryFn<typeof MessageComposerToolbarActions> = () => <MessageToolbarActions />;
-
-export const _MessageComposer: StoryFn<typeof MessageComposer> = () => (
+export const Default: StoryFn<typeof MessageComposer> = () => (
 	<MessageComposer>
 		<MessageComposerInput placeholder='Text' />
 		<MessageComposerToolbar>
@@ -51,7 +48,9 @@ export const _MessageComposer: StoryFn<typeof MessageComposer> = () => (
 	</MessageComposer>
 );
 
-export const MessageComposerWithHints: StoryFn<typeof MessageComposer> = () => (
+export const ToolbarActions: StoryFn<typeof MessageComposerToolbarActions> = () => <MessageToolbarActions />;
+
+export const WithHints: StoryFn<typeof MessageComposer> = () => (
 	<>
 		<MessageComposerHint
 			icon='pencil'
@@ -64,7 +63,7 @@ export const MessageComposerWithHints: StoryFn<typeof MessageComposer> = () => (
 			Editing message
 		</MessageComposerHint>
 		<MessageComposer>
-			<MessageComposerInput placeholder='Text' value='Lorem ipsum dolor' />
+			<MessageComposerInput placeholder='Text' />
 			<MessageComposerToolbar>
 				<MessageToolbarActions />
 				<MessageComposerToolbarSubmit>
@@ -75,7 +74,7 @@ export const MessageComposerWithHints: StoryFn<typeof MessageComposer> = () => (
 	</>
 );
 
-export const MessageComposerWithSubmitActions: StoryFn<typeof MessageComposer> = () => (
+export const WithSubmit: StoryFn<typeof MessageComposer> = () => (
 	<MessageComposer>
 		<MessageComposerInput placeholder='Text' />
 		<MessageComposerToolbar>
@@ -90,4 +89,4 @@ export const MessageComposerWithSubmitActions: StoryFn<typeof MessageComposer> =
 	</MessageComposer>
 );
 
-export const MessageComposerLoading: StoryFn<typeof MessageComposer> = () => <MessageComposerSkeleton />;
+export const Loading: StoryFn<typeof MessageComposer> = () => <MessageComposerSkeleton />;
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerHint.spec.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerHint.spec.tsx
new file mode 100644
index 0000000000000..57603e72cb3f4
--- /dev/null
+++ b/packages/ui-composer/src/MessageComposer/MessageComposerHint.spec.tsx
@@ -0,0 +1,19 @@
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import * as stories from './MessageComposerHint.stories';
+
+const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
+
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+	const tree = render(<Story />);
+	expect(tree.baseElement).toMatchSnapshot();
+});
+
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+	const { container } = render(<Story />);
+
+	const results = await axe(container);
+	expect(results).toHaveNoViolations();
+});
diff --git a/packages/ui-composer/src/MessageComposer/MessageComposerHint.stories.tsx b/packages/ui-composer/src/MessageComposer/MessageComposerHint.stories.tsx
index bf423bf47139f..ed0e821509750 100644
--- a/packages/ui-composer/src/MessageComposer/MessageComposerHint.stories.tsx
+++ b/packages/ui-composer/src/MessageComposer/MessageComposerHint.stories.tsx
@@ -4,7 +4,7 @@ import { MessageComposerHint } from '.';
 import '@rocket.chat/icons/dist/rocketchat.css';
 
 export default {
-	title: 'Components/MessageComposer/Hint',
+	title: 'Components/MessageComposerHint',
 	component: MessageComposerHint,
 } as Meta<typeof MessageComposerHint>;
 
diff --git a/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap b/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap
new file mode 100644
index 0000000000000..b963e86b2f732
--- /dev/null
+++ b/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposer.spec.tsx.snap
@@ -0,0 +1,850 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders Default without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-input-box__wrapper rcx-css-1t5etly"
+      role="group"
+    >
+      <div
+        class="rcx-box rcx-box--full rcx-css-1drwzbn"
+      >
+        <textarea
+          class="rcx-box rcx-box--full rcx-css-1fg3rdf rc-message-box__textarea js-input-message rcx-css-1hzjarz"
+          placeholder="Text"
+          rows="1"
+        />
+      </div>
+      <div
+        class="rcx-box rcx-box--full rcx-css-jyts6z"
+      >
+        <div
+          aria-orientation="horizontal"
+          class="rcx-button-group rcx-button-group--align-start rcx-button-group--small"
+          role="toolbar"
+        >
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="emoji"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-emoji rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <hr
+            class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+          />
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="bold"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-bold rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="italic"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-italic rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="underline"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-underline rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="strike"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-strike rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="code"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-code rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="multiline"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-multiline rcx-icon rcx-css-4pvxx3"
+            >
+              ⁅
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="link"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-link rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="katex"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-katex rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <hr
+            class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+          />
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="mic"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-mic rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="video"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-video rcx-icon rcx-css-4pvxx3"
+            >
+              ❲
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="attachment"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-clip rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="more"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-plus rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders Loading without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-input-box__wrapper rcx-css-1t5etly"
+      role="group"
+    >
+      <div
+        class="rcx-box rcx-box--full rcx-css-1attbr1"
+      >
+        <span
+          class="rcx-skeleton rcx-skeleton--text rcx-css-17gdwyr"
+        />
+      </div>
+      <div
+        class="rcx-box rcx-box--full rcx-css-1ifbqvi"
+      />
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders ToolbarActions without crashing 1`] = `
+<body>
+  <div>
+    <div
+      aria-orientation="horizontal"
+      class="rcx-button-group rcx-button-group--align-start rcx-button-group--small"
+      role="toolbar"
+    >
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="emoji"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-emoji rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <hr
+        class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+      />
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="bold"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-bold rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="italic"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-italic rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="underline"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-underline rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="strike"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-strike rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="code"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-code rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="multiline"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-multiline rcx-icon rcx-css-4pvxx3"
+        >
+          ⁅
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="link"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-link rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="katex"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-katex rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <hr
+        class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+      />
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="mic"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-mic rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="video"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-video rcx-icon rcx-css-4pvxx3"
+        >
+          ❲
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="attachment"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-clip rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+        title="more"
+        type="button"
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-plus rcx-icon rcx-css-4pvxx3"
+        >
+          
+        </i>
+      </button>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders WithHints without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-r3aouy"
+    >
+      <span
+        class="rcx-box rcx-box--full rcx-tag "
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-pencil rcx-icon rcx-css-nbjs1j"
+        >
+          ⦏
+        </i>
+        <span
+          class="rcx-tag__inner"
+        >
+          Editing message
+        </span>
+      </span>
+      <div
+        class="rcx-box rcx-box--full rcx-css-1c8uhnw"
+      >
+        <strong>
+          esc
+        </strong>
+         to cancel · 
+        <strong>
+          enter
+        </strong>
+         to save
+      </div>
+    </div>
+    <div
+      class="rcx-box rcx-box--full rcx-input-box__wrapper rcx-css-1t5etly"
+      role="group"
+    >
+      <div
+        class="rcx-box rcx-box--full rcx-css-1drwzbn"
+      >
+        <textarea
+          class="rcx-box rcx-box--full rcx-css-1fg3rdf rc-message-box__textarea js-input-message rcx-css-1hzjarz"
+          placeholder="Text"
+          rows="1"
+        />
+      </div>
+      <div
+        class="rcx-box rcx-box--full rcx-css-jyts6z"
+      >
+        <div
+          aria-orientation="horizontal"
+          class="rcx-button-group rcx-button-group--align-start rcx-button-group--small"
+          role="toolbar"
+        >
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="emoji"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-emoji rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <hr
+            class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+          />
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="bold"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-bold rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="italic"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-italic rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="underline"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-underline rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="strike"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-strike rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="code"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-code rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="multiline"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-multiline rcx-icon rcx-css-4pvxx3"
+            >
+              ⁅
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="link"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-link rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="katex"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-katex rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <hr
+            class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+          />
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="mic"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-mic rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="video"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-video rcx-icon rcx-css-4pvxx3"
+            >
+              ❲
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="attachment"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-clip rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="more"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-plus rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+        </div>
+        <div
+          class="rcx-button-group rcx-button-group--align-start rcx-button-group--small"
+          role="group"
+        >
+          <button
+            aria-label="Send"
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--icon-secondary-info rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-send rcx-icon rcx-css-4pvxx3"
+            >
+              〈
+            </i>
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders WithSubmit without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-input-box__wrapper rcx-css-1t5etly"
+      role="group"
+    >
+      <div
+        class="rcx-box rcx-box--full rcx-css-1drwzbn"
+      >
+        <textarea
+          class="rcx-box rcx-box--full rcx-css-1fg3rdf rc-message-box__textarea js-input-message rcx-css-1hzjarz"
+          placeholder="Text"
+          rows="1"
+        />
+      </div>
+      <div
+        class="rcx-box rcx-box--full rcx-css-jyts6z"
+      >
+        <div
+          aria-orientation="horizontal"
+          class="rcx-button-group rcx-button-group--align-start rcx-button-group--small"
+          role="toolbar"
+        >
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="emoji"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-emoji rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <hr
+            class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+          />
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="bold"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-bold rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="italic"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-italic rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="underline"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-underline rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="strike"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-strike rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="code"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-code rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="multiline"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-multiline rcx-icon rcx-css-4pvxx3"
+            >
+              ⁅
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="link"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-link rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="katex"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-katex rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <hr
+            class="rcx-box rcx-box--full rcx-divider--vertical rcx-divider rcx-button-group__item rcx-css-e8uboy"
+          />
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="mic"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-mic rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="video"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-video rcx-icon rcx-css-4pvxx3"
+            >
+              ❲
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="attachment"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-clip rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small-square rcx-button--square rcx-button--icon rcx-button rcx-button-group__item"
+            title="more"
+            type="button"
+          >
+            <i
+              aria-hidden="true"
+              class="rcx-box rcx-box--full rcx-icon--name-plus rcx-icon rcx-css-4pvxx3"
+            >
+              
+            </i>
+          </button>
+        </div>
+        <div
+          class="rcx-button-group rcx-button-group--align-start rcx-button-group--small"
+          role="group"
+        >
+          <button
+            class="rcx-box rcx-box--full rcx-button--small rcx-button rcx-button-group__item"
+            type="button"
+          >
+            <span
+              class="rcx-button--content"
+            >
+              Preview
+            </span>
+          </button>
+          <button
+            class="rcx-box rcx-box--full rcx-button--small rcx-button--primary rcx-button rcx-button-group__item"
+            type="button"
+          >
+            <span
+              class="rcx-button--content"
+            >
+              Send
+            </span>
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+</body>
+`;
diff --git a/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposerHint.spec.tsx.snap b/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposerHint.spec.tsx.snap
new file mode 100644
index 0000000000000..0636aa405840c
--- /dev/null
+++ b/packages/ui-composer/src/MessageComposer/__snapshots__/MessageComposerHint.spec.tsx.snap
@@ -0,0 +1,85 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders HintWithIcon without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-r3aouy"
+    >
+      <span
+        class="rcx-box rcx-box--full rcx-tag "
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-eye rcx-icon rcx-css-nbjs1j"
+        >
+          
+        </i>
+        <span
+          class="rcx-tag__inner"
+        >
+          This room is read only
+        </span>
+      </span>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders HintWithIconAndHelperText without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-r3aouy"
+    >
+      <span
+        class="rcx-box rcx-box--full rcx-tag "
+      >
+        <i
+          aria-hidden="true"
+          class="rcx-box rcx-box--full rcx-icon--name-pencil rcx-icon rcx-css-nbjs1j"
+        >
+          ⦏
+        </i>
+        <span
+          class="rcx-tag__inner"
+        >
+          Editing message
+        </span>
+      </span>
+      <div
+        class="rcx-box rcx-box--full rcx-css-1c8uhnw"
+      >
+        <strong>
+          esc
+        </strong>
+         to cancel · 
+        <strong>
+          enter
+        </strong>
+         to save
+      </div>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders HintWithText without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-r3aouy"
+    >
+      <span
+        class="rcx-box rcx-box--full rcx-tag "
+      >
+        <span
+          class="rcx-tag__inner"
+        >
+          You're sending an unencrypted message
+        </span>
+      </span>
+    </div>
+  </div>
+</body>
+`;
diff --git a/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.spec.tsx b/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.spec.tsx
new file mode 100644
index 0000000000000..76f3bca2a145c
--- /dev/null
+++ b/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.spec.tsx
@@ -0,0 +1,19 @@
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import * as stories from './MessageFooterCallout.stories';
+
+const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
+
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+	const tree = render(<Story />);
+	expect(tree.baseElement).toMatchSnapshot();
+});
+
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+	const { container } = render(<Story />);
+
+	const results = await axe(container);
+	expect(results).toHaveNoViolations();
+});
diff --git a/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.stories.tsx b/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.stories.tsx
index 4e55cea3761e3..c7893d8248e66 100644
--- a/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.stories.tsx
+++ b/packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.stories.tsx
@@ -9,7 +9,7 @@ import MessageComposerIcon from '../MessageComposer/MessageComposerIcon';
 import '@rocket.chat/icons/dist/rocketchat.css';
 
 export default {
-	title: 'Components/MessageComposer/Locked',
+	title: 'Components/MessageComposerCallout',
 	component: MessageComposer,
 } satisfies Meta<typeof MessageComposer>;
 
diff --git a/packages/ui-composer/src/MessageFooterCallout/__snapshots__/MessageFooterCallout.spec.tsx.snap b/packages/ui-composer/src/MessageFooterCallout/__snapshots__/MessageFooterCallout.spec.tsx.snap
new file mode 100644
index 0000000000000..148ce20b57b89
--- /dev/null
+++ b/packages/ui-composer/src/MessageFooterCallout/__snapshots__/MessageFooterCallout.spec.tsx.snap
@@ -0,0 +1,108 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders _MessageFooterCalloutAction without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-1rv8koo"
+    >
+      Feedback text 
+      <hr
+        class="rcx-box rcx-box--full rcx-css-vi5s2c"
+      />
+      <button
+        class="rcx-box rcx-box--full rcx-button--small rcx-button--primary rcx-button rcx-css-1dy74d9"
+        type="button"
+      >
+        <span
+          class="rcx-button--content"
+        >
+          Button
+        </span>
+      </button>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders MessageComposerBlocked without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-1rv8koo"
+    >
+      <i
+        aria-hidden="true"
+        class="rcx-box rcx-box--full rcx-icon--name-burger rcx-icon rcx-css-1c32rho"
+      >
+        
+      </i>
+      Feedback text
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders MessageComposerBlockedLargeText without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-1rv8koo"
+    >
+      <i
+        aria-hidden="true"
+        class="rcx-box rcx-box--full rcx-icon--name-burger rcx-icon rcx-css-1c32rho"
+      >
+        
+      </i>
+      <div
+        class="rcx-box rcx-box--full rcx-css-lp8q3w"
+      >
+        Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text text Feedback text Feedback text Feedback text Feedback text Feedback text text Feedback text Feedback text Feedback text Feedback text Feedback text text Feedback text Feedback text Feedback text Feedback text Feedback text
+      </div>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small rcx-button--primary rcx-button rcx-css-1dy74d9"
+        type="button"
+      >
+        <span
+          class="rcx-button--content"
+        >
+          Button
+        </span>
+      </button>
+    </div>
+  </div>
+</body>
+`;
+
+exports[`renders MessageComposerBlockedLargeTextDashed without crashing 1`] = `
+<body>
+  <div>
+    <div
+      class="rcx-box rcx-box--full rcx-css-111uahs"
+    >
+      <i
+        aria-hidden="true"
+        class="rcx-box rcx-box--full rcx-icon--name-burger rcx-icon rcx-css-1c32rho"
+      >
+        
+      </i>
+      <div
+        class="rcx-box rcx-box--full rcx-css-lp8q3w"
+      >
+        Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text Feedback text text Feedback text Feedback text Feedback text Feedback text Feedback text text Feedback text Feedback text Feedback text Feedback text Feedback text text Feedback text Feedback text Feedback text Feedback text Feedback text
+      </div>
+      <button
+        class="rcx-box rcx-box--full rcx-button--small rcx-button--primary rcx-button rcx-css-1dy74d9"
+        type="button"
+      >
+        <span
+          class="rcx-button--content"
+        >
+          Button
+        </span>
+      </button>
+    </div>
+  </div>
+</body>
+`;
diff --git a/packages/ui-composer/tsconfig.build.json b/packages/ui-composer/tsconfig.build.json
index fa5e0252857bc..56dd97b582908 100644
--- a/packages/ui-composer/tsconfig.build.json
+++ b/packages/ui-composer/tsconfig.build.json
@@ -1,4 +1,4 @@
 {
 	"extends": "./tsconfig.json",
-	"exclude": ["./src/**/*.stories.tsx", "./.storybook/**/*", "./jest.config.ts"]
+	"exclude": ["**/*.stories.tsx", "./src/**/*.spec.ts", "./src/**/*.spec.tsx", "./.storybook/**/*", "./jest.config.ts"]
 }
diff --git a/yarn.lock b/yarn.lock
index ede67b586f410..ed30312bec64f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9654,6 +9654,8 @@ __metadata:
     "@rocket.chat/eslint-config": "workspace:^"
     "@rocket.chat/fuselage": "npm:~0.62.0"
     "@rocket.chat/icons": "npm:^0.42.0"
+    "@rocket.chat/jest-presets": "workspace:~"
+    "@storybook/addon-a11y": "npm:^8.6.4"
     "@storybook/addon-actions": "npm:^8.6.4"
     "@storybook/addon-docs": "npm:^8.6.4"
     "@storybook/addon-essentials": "npm:^8.6.4"
@@ -9661,12 +9663,14 @@ __metadata:
     "@storybook/addon-webpack5-compiler-babel": "npm:^3.0.5"
     "@storybook/react": "npm:^8.6.4"
     "@storybook/react-webpack5": "npm:^8.6.4"
+    "@types/jest": "npm:~29.5.14"
     "@types/react": "npm:~18.3.17"
     "@types/react-dom": "npm:~18.3.5"
     eslint: "npm:~8.45.0"
     eslint-plugin-react: "npm:~7.37.2"
     eslint-plugin-react-hooks: "npm:~5.0.0"
     eslint-plugin-storybook: "npm:^0.11.4"
+    jest: "npm:~29.7.0"
     react: "npm:~18.3.1"
     react-dom: "npm:~18.3.1"
     storybook: "npm:^8.6.4"


Reviews:
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-06T15:44:28Z
user: login: kody-ai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-06T16:48:37Z

Review comments:
diff_hunk: @@ -0,0 +1,19 @@
+import { composeStories } from '@storybook/react';
+import { render } from '@testing-library/react';
+import { axe } from 'jest-axe';
+
+import * as stories from './MessageComposer.stories';
+
+const testCases = Object.values(composeStories(stories)).map((Story) => [Story.storyName || 'Story', Story]);
+
+test.each(testCases)(`renders %s without crashing`, async (_storyname, Story) => {
+	const tree = render(<Story />);
+	expect(tree.baseElement).toMatchSnapshot();
+});
+
+test.each(testCases)('%s should have no a11y violations', async (_storyname, Story) => {
+	const { container } = render(<Story />);
+
+	const results = await axe(container);
+	expect(results).toHaveNoViolations();
path: packages/ui-composer/src/MessageComposer/MessageComposer.spec.tsx
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Error Handling](https://img.shields.io/badge/Error_Handling-FF6D00) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
try {
  const results = await axe(container);
  expect(results).toHaveNoViolations();
} catch (error) {
  console.error('Accessibility test failed:', error);
  throw error;
}
```
Accessibility tests lack error handling, which could lead to uncaught errors during the accessibility check.

This issue appears in multiple locations:
* packages/ui-composer/src/MessageComposer/MessageComposer.spec.tsx: Lines 17-18
* packages/ui-composer/src/MessageFooterCallout/MessageFooterCallout.spec.tsx: Lines 17-18
Please add error handling to catch and log potential errors during accessibility tests.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-06T15:44:28Z
author_association: NONE
diff_hunk: @@ -1,4 +1,4 @@
 {
 	"extends": "./tsconfig.json",
-	"exclude": ["./src/**/*.stories.tsx", "./.storybook/**/*", "./jest.config.ts"]
+	"exclude": ["**/*.stories.tsx", "./src/**/*.spec.ts", "./src/**/*.spec.tsx", "./.storybook/**/*", "./jest.config.ts"]
path: packages/ui-composer/tsconfig.build.json
user: login: kody-ai[bot]
body: ![kody code-review](https://img.shields.io/badge/kody-code--review-312B4B?labelColor=C9BBF2) ![Maintainability](https://img.shields.io/badge/Maintainability-0091EA) ![medium](https://img.shields.io/badge/severity_level-medium-1A7BBE)
```typescript
"exclude": ["./src/**/*.stories.tsx", "./src/**/*.spec.ts", "./src/**/*.spec.tsx", "./.storybook/**/*", "./jest.config.ts"]
```
Inconsistent path patterns in the 'exclude' array of the tsconfig.build.json file, which could lead to confusion and maintainability issues.

This issue appears in multiple locations:
* packages/ui-composer/tsconfig.build.json: Lines 3-3
* packages/ui-composer/tsconfig.build.json: Lines 3-3
Please standardize all path patterns in the 'exclude' array to use './src/**/*' for consistency and better maintainability.


<sub>Talk to Kody by mentioning @kody</sub>


<sub>Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.</sub>

<!-- kody-codereview -->&#8203;
&#8203;
created_at: 2025-05-06T16:48:37Z
author_association: NONE

PR comments:
user: login: dionisio-bot[bot]
created_at: 2025-05-06T15:42:10Z
body: Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the [PR guidelines](https://handbook.rocket.chat/departments-and-operations/research-and-development/engineering/development/pr-general-instructions-and-handling)
author_association: CONTRIBUTOR
user: login: kody-ai[bot]
created_at: 2025-05-06T15:42:14Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-06T15:49:12Z
body: [PR Preview Action](https://github.com/rossjrw/pr-preview-action) v1.6.1
:---:
| <p></p> :rocket: View preview at <br> https://RocketChat.github.io/Rocket.Chat/pr-preview/pr-35932/ <br><br>
| <h6>Built to branch [`gh-pages`](https://github.com/RocketChat/Rocket.Chat/tree/gh-pages) at 2025-05-07 13:23 UTC. <br> Preview will be ready when the [GitHub Pages deployment](https://github.com/RocketChat/Rocket.Chat/deployments) is complete. <br><br> </h6>
<!-- Sticky Pull Request Commentpr-preview -->
author_association: CONTRIBUTOR
user: login: changeset-bot[bot]
created_at: 2025-05-06T15:51:13Z
body: ###  ⚠️  No Changeset found

Latest commit: c1333821caad37704705d3f1f326c210cf7ed401

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/RocketChat/Rocket.Chat/new/refactor/message-composer-input?filename=.changeset/proud-rings-roll.md&value=---%0A%22%40rocket.chat%2Fui-composer%22%3A%20patch%0A---%0A%0Atest%3A%20Adopt%20jest%20testing%20in%20%60ui-composer%60%0A)


author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-05-06T15:51:50Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: codecov[bot]
created_at: 2025-05-06T16:41:19Z
body: ## [Codecov](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35932?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 61.34%. Comparing base [(`9b0d87a`)](https://app.codecov.io/gh/RocketChat/Rocket.Chat/commit/9b0d87a3e2596a5d948de07071b014e7aee3f7b5?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) to head [(`c133382`)](https://app.codecov.io/gh/RocketChat/Rocket.Chat/commit/c1333821caad37704705d3f1f326c210cf7ed401?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat).
> Report is 12 commits behind head on develop.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35932/graphs/tree.svg?width=650&height=150&src=pr&token=lEAH159Fca&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat)](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35932?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat)

```diff
@@             Coverage Diff             @@
##           develop   #35932      +/-   ##
===========================================
+ Coverage    61.29%   61.34%   +0.04%     
===========================================
  Files         3164     3184      +20     
  Lines        74796    74930     +134     
  Branches     16693    16698       +5     
===========================================
+ Hits         45847    45966     +119     
- Misses       25849    25859      +10     
- Partials      3100     3105       +5     
```

| [Flag](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35932/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) | Coverage Δ | |
|---|---|---|
| [e2e](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35932/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) | `58.07% <ø> (-0.02%)` | :arrow_down: |
| [unit](https://app.codecov.io/gh/RocketChat/Rocket.Chat/pull/35932/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat) | `75.22% <100.00%> (+0.10%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=RocketChat#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-05-06T16:46:13Z
body: ## Code Review Completed! 🔥

The code review was successfully completed based on your current configurations.



<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-05-06T18:03:57Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-05-06T18:43:25Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-05-07T13:14:06Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE
user: login: kody-ai[bot]
created_at: 2025-05-07T13:18:58Z
body: # Kody Review Complete
**Great news!** 🎉
No issues were found that match your current review configurations.

Keep up the excellent work! 🚀

<details>
<summary>Kody Guide: Usage and Configuration</summary>

<details>
<summary>Interacting with Kody</summary>

- **Request a Review:** Ask Kody to review your PR manually by adding a comment with the `@kody start-review` command at the root of your PR.

- **Provide Feedback:** Help Kody learn and improve by reacting to its comments with a 👍 for helpful suggestions or a 👎 if improvements are needed.

</details>

<details>
<summary>Current Kody Configuration</summary>

<details>
<summary>Review Options</summary>

The following review options are enabled or disabled:

| Options                        | Enabled |
|-------------------------------|---------|
| **Security** | ✅ |
| **Code Style** | ✅ |
| **Kody Rules** | ✅ |
| **Refactoring** | ✅ |
| **Error Handling** | ✅ |
| **Maintainability** | ✅ |
| **Potential Issues** | ✅ |
| **Documentation And Comments** | ✅ |
| **Performance And Optimization** | ✅ |
| **Breaking Changes** | ❌ |

</details>

**[Access your configuration settings here.](https://app.kodus.io/settings/code-review/global/general)**

</details>
</details>

<!-- kody-codereview -->
&#8203;
author_association: NONE