Skip to content

Comments

fix(ui): prevent horizontal overflow on home page mobile#38421

Closed
sharanyamahajan wants to merge 20 commits intoRocketChat:developfrom
sharanyamahajan:fix/home-mobile-overflow
Closed

fix(ui): prevent horizontal overflow on home page mobile#38421
sharanyamahajan wants to merge 20 commits intoRocketChat:developfrom
sharanyamahajan:fix/home-mobile-overflow

Conversation

@sharanyamahajan
Copy link

@sharanyamahajan sharanyamahajan commented Jan 29, 2026

Proposed changes (including videos or screenshots)

This PR fixes an issue where the Home page caused unwanted horizontal scrolling on small mobile screens.

The fix ensures that the Home page root container, header, and grid items respect the viewport width on mobile devices. Overflow protection is applied at the component level to prevent elements from exceeding the horizontal bounds of the screen.

This improves the mobile user experience without impacting the desktop layout or existing functionality.

Issue(s)

Related to #38398

Steps to test or reproduce

  1. Run the application locally.
  2. Open the Home page.
  3. Open browser DevTools and switch to mobile view (≤360px width).
  4. Try scrolling horizontally.

Expected result:

  • No horizontal scrolling on mobile screens.
  • Desktop layout remains unchanged.

Further comments

The fix is intentionally scoped to the Home page components instead of using a global overflow rule. This avoids hiding unintended overflows elsewhere in the application and keeps the change minimal and safe.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced registration form with improved validation and error messaging.
    • Profile form now automatically trims whitespace from input fields for cleaner data.
  • Bug Fixes

    • Fixed homepage and layout styling to prevent horizontal overflow.
    • Improved sidebar item removal safety.
  • Documentation

    • Added architecture overview documentation for contributors.

✏️ Tip: You can customize this high-level summary in your review settings.

fix(admin): make Custom HTML card responsive on mobile
…n-loader

fix(ui): add skeleton loading states to Room Contextual Bar views
…ptions

fix: prevent startup crash on invalid MONGO_OPTIONS JSON
refactor: reduce complexity in RegisterForm
@sharanyamahajan sharanyamahajan requested review from a team as code owners January 29, 2026 16:53
@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 29, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@CLAassistant
Copy link

CLAassistant commented Jan 29, 2026

CLA assistant check
All committers have signed the CLA.

@changeset-bot
Copy link

changeset-bot bot commented Jan 29, 2026

⚠️ No Changeset found

Latest commit: 5455a1e

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.

This PR includes no changesets

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

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 29, 2026

Walkthrough

This PR introduces robustness improvements across multiple subsystems: caching logic with initialization and synchronization, profile form field trimming with simplified rendering, livechat store type strengthening and component modernization, sidebar item registration guards, home page layout overflow containment, data model consolidation, registration form validation hooks, and supporting documentation and test utilities.

Changes

Cohort / File(s) Summary
CachedStore robustness
apps/meteor/client/lib/cachedStores/CachedStore.ts
Overhaul of client-side caching: added initialization flow (init, performInitialization, initializationPromise), improved deserialization with strict type casting, tightened sync logic with explicit action ordering and null checks, added offline/reconnection handling via Tracker.autorun, and modified PublicCachedStore.getToken() to return 'public' instead of undefined. Cache validation now enforces max-age and token checks.
Sidebar item registration
apps/meteor/client/lib/createSidebarItems.ts
Added guard against invalid index (index === -1) in unregisterSidebarItem to prevent out-of-bounds removal; uses splice instead of delete. Minor type formatting for SidebarDivider.
Profile form refactoring
apps/meteor/client/views/account/profile/AccountProfileForm.tsx, apps/meteor/client/views/account/profile/getProfileInitialValues.ts
Consolidated form handling with centralized field trimming (email, name, username, statusText, bio, nickname) via trimmedValues object; simplified field rendering by removing excessive ARIA attributes and helper text; reorganized layout with avatar preceding form sections.
Home page layout containment
apps/meteor/client/views/home/HomePage.tsx, apps/meteor/client/views/home/HomePageHeader.tsx, apps/meteor/client/views/home/HomepageGridItem.tsx
Wrapped page components in divs with width/max-width/overflow controls to constrain horizontal scrolling; applied consistent styling across home page sections.
Livechat component modernization
packages/livechat/src/components/Menu/index.tsx
Refactored PopoverMenuWrapper from class-based to functional component using React hooks (useRef, useState, useLayoutEffect); maintains equivalent behavior with position state computation and placement calculation.
Livechat store type strengthening
packages/livechat/src/store/index.tsx, packages/livechat/src/store/types.ts
Added new public types (LivechatAlert, LivechatQueueInfo, LivechatConfig, LivechatModalState); replaced loose any types with strict interfaces; expanded StoreState shape with explicit config, iframe, and agent fields; removed legacy window event handlers; simplified StoreContext API with explicit typing.
Data model consolidation
packages/models/src/models/BaseRaw.ts
Consolidated findOne overloads into single generic signature; inlined _id generation in upsert paths; refactored trash/soft-delete logic with rollback error handling; removed deprecated method variants to simplify public surface.
MongoDB config safety
apps/meteor/packages/rocketchat-mongo-config/server/index.js
Added optional chaining for safe MONGO_URL access; improved error logging for invalid JSON with detailed error messages before throwing.
Registration form validation extraction
packages/web-ui-registration/src/RegisterForm.tsx, packages/web-ui-registration/src/hooks/useRegisterValidation.ts, packages/web-ui-registration/src/hooks/useRegisterErrorHandling.ts
Extracted inline field validation into useRegisterValidation hook; centralized error handling into useRegisterErrorHandling hook; supports field-specific error messages, server error propagation, and navigation on activation errors.
Documentation and test helpers
docs/architecture/overview.md, ee/packages/license/__tests__/MockedLicenseBuilder.*, apps/meteor/client/views/room/contextualBar/lib/ContextualListSkeleton.tsx
Added architecture overview documentation; created license test helper (MockedLicenseBuilder.d.ts/.js) for mocked LicenseImp instances; introduced ContextualListSkeleton component for loading states with configurable item count.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • #36911 — Overlapping modifications to CachedStore initialization, event handling, and sync logic.

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • sampaiodiego
  • cardoso
  • ggazzo

Poem

🐰 A warren of changes, from cache to form,
Trimming whitespace and types, the standard norm,
Hooks extracted, stores refined with care,
The rabbit hops forward—improvements in the air! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(ui): prevent horizontal overflow on home page mobile' accurately describes the main objective of preventing horizontal overflow on mobile devices for the Home page.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/client/views/account/profile/AccountProfileForm.tsx (1)

90-101: Add required field validation and trim username input before validation.

Empty usernames pass validation because the validator returns undefined (no error) and the field has no required rule. Additionally, trimming happens in handleSave (line 110) after validation, meaning whitespace-only values would be validated as entered. Trim the username at the start of validation and return a required field error if empty.

Proposed fix
const validateUsername = async (username: string): Promise<string | undefined> => {
+	const normalized = username?.trim() || '';
+	if (!normalized) {
+		return t('Required_field', { field: t('Username') });
+	}
-	if (!username || username === previousUsername) {
+	if (normalized === previousUsername) {
 		return;
 	}
 
-	if (!namesRegex.test(username)) {
+	if (!namesRegex.test(normalized)) {
 		return t('error-invalid-username');
 	}
 
-	const { result: isAvailable } = await checkUsernameAvailability({ username });
+	const { result: isAvailable } = await checkUsernameAvailability({ username: normalized });
 	if (!isAvailable) {
 		return t('Username_already_exist');
 	}
};
🤖 Fix all issues with AI agents
In `@apps/meteor/client/views/account/profile/AccountProfileForm.tsx`:
- Around line 238-270: The FieldLabel htmlFor values (nicknameId, bioId,
emailId) were left without matching input ids, breaking label association;
update the Controller renderers to pass the corresponding id prop to the inputs
(e.g. in the nickname Controller render pass id={nicknameId} to TextInput, in
the bio Controller pass id={bioId} to TextAreaInput, and in the email Controller
pass id={emailId} to TextInput while preserving disabled={!allowEmailChange} and
existing validation rules). Ensure you still spread the controller's field props
({...field}) so value/onChange remain wired.
🧹 Nitpick comments (10)
apps/meteor/client/views/home/HomePageHeader.tsx (1)

15-29: Minor: width: '100%' is redundant for block-level elements.

The div is a block element and will naturally take 100% width of its container. Only maxWidth: '100%' and overflowX: 'hidden' are needed to achieve the overflow fix.

♻️ Suggested simplification
 		<div
 			style={{
-				width: '100%',
 				maxWidth: '100%',
 				overflowX: 'hidden',
 			}}
 		>
apps/meteor/client/views/home/HomePage.tsx (1)

10-20: Minor: Same redundant width: '100%' as in HomePageHeader.

For consistency, consider removing width: '100%' here as well since block elements inherit full width by default.

♻️ Suggested simplification
 	return (
 		<div
 			style={{
-				width: '100%',
 				maxWidth: '100%',
 				overflowX: 'hidden',
 			}}
 		>
docs/architecture/overview.md (1)

25-29: Minor: Markdown table formatting.

Static analysis flags spacing inconsistencies in the table. Consider using consistent spacing around pipe characters for better readability.

📝 Suggested fix
-| Directory | Purpose | Runtime role |
-|---------|--------|--------------|
-| `apps/` | Entry points and deployable applications | Client and server applications |
-| `packages/` | Core and shared functionality | Loaded by Meteor at runtime |
-| `ee/` | Enterprise-specific features and services | Optional, license-gated runtime modules |
+| Directory    | Purpose                                   | Runtime role                            |
+|--------------|-------------------------------------------|-----------------------------------------|
+| `apps/`      | Entry points and deployable applications  | Client and server applications          |
+| `packages/`  | Core and shared functionality             | Loaded by Meteor at runtime             |
+| `ee/`        | Enterprise-specific features and services | Optional, license-gated runtime modules |
packages/livechat/src/store/types.ts (1)

31-31: Remove inline comment per coding guidelines.

The comment // unknown > any (safer) should be removed. As per coding guidelines: "Avoid code comments in the implementation."

Suggested fix
-  payload?: unknown; // unknown > any (safer)
+  payload?: unknown;
packages/models/src/models/BaseRaw.ts (1)

170-170: Remove banner comments per coding guidelines.

The section marker comments (/* ===================== FIXED METHOD ===================== */ and the closing marker) should be removed. As per coding guidelines: "Avoid code comments in the implementation."

Suggested fix
-	/* ===================== FIXED METHOD ===================== */
-
 	async findOneAndDelete(filter: Filter<T>, options?: FindOneAndDeleteOptions): Promise<WithId<T> | null> {
-	/* ======================================================== */
-
 	private setUpdatedAt(record: UpdateFilter<T> | InsertionModel<T>): void {

Also applies to: 222-222

packages/web-ui-registration/src/hooks/useRegisterValidation.ts (1)

32-39: Consider typing the values parameter more explicitly.

The values parameter is typed as any. For better type safety, consider using the form's payload type:

♻️ Optional type improvement
-			validate: (val: string, values: any) =>
+			validate: (val: string, values: { password: string }) =>
 				values.password === val ? true : t('registration.component.form.invalidConfirmPass'),
packages/web-ui-registration/src/hooks/useRegisterErrorHandling.ts (3)

4-8: Consider typing setError more explicitly.

The setError parameter is typed as any, which loses the benefits of TypeScript's type checking. Consider using the UseFormSetError type from react-hook-form:

♻️ Suggested type improvement
+import type { UseFormSetError } from 'react-hook-form';
+
+type FormFields = 'email' | 'username' | 'name';
+
 type Params = {
-	setError: any;
+	setError: UseFormSetError<Record<FormFields, string>>;
 	setLoginRoute: (route: 'login') => void;
 	setServerError: (value: string | undefined) => void;
 };

Alternatively, if the form's full type is available, use that directly for complete type safety.


14-54: Consider adding a fallback for unhandled errors.

The error handler covers many specific cases but has no fallback for unexpected error types. While the mutation's error handling may cover this upstream, consider adding a catch-all to ensure visibility:

♻️ Optional fallback suggestion
 		if (error.error === 'error-user-registration-custom-field') {
 			setServerError(error.message);
 		}
+
+		// Log unhandled errors for debugging (optional)
+		const handledErrors = [
+			'error-invalid-email',
+			'error-user-already-exists',
+			'error-too-many-requests',
+			'error-user-is-not-activated',
+			'error-user-registration-custom-field',
+		];
+		if (!handledErrors.some((e) => error.error?.includes(e) || error.errorType?.includes(e))) {
+			console.warn('Unhandled registration error:', error);
+		}
 	};

23-40: String-based error matching is fragile.

The regex tests like /Email already exists/ rely on server error message text. If server messages change, these handlers will silently stop working. Consider requesting structured error codes from the server if not already available, or document this dependency.

apps/meteor/client/views/account/profile/AccountProfileForm.tsx (1)

155-156: Remove inline JSX comments to match repo guidance.

These section headers are now redundant given the layout structure.

🧹 Proposed cleanup
-				{/* Avatar */}
 				<Field>
-				{/* Name + Username */}
 				<Box display='flex' flexDirection={isMobile ? 'column' : 'row'} gap='x16'>
-				{/* Status */}
 				<Field>
-				{/* Nickname */}
 				<Field>
-				{/* Bio */}
 				<Field>
-				{/* Email */}
 				<Field>
As per coding guidelines: Avoid code comments in the implementation.

Also applies to: 173-174, 210-211, 238-239, 246-247, 259-260

Comment on lines +238 to 270
{/* Nickname */}
<Field>
<FieldLabel htmlFor={nicknameId}>{t('Nickname')}</FieldLabel>
<FieldRow>
<Controller
control={control}
name='nickname'
render={({ field }) => (
<TextInput {...field} id={nicknameId} flexGrow={1} addon={<Icon name='edit' size='x20' alignSelf='center' />} />
)}
/>
<Controller control={control} name='nickname' render={({ field }) => <TextInput {...field} />} />
</FieldRow>
</Field>

{/* Bio */}
<Field>
<FieldLabel htmlFor={bioId}>{t('Bio')}</FieldLabel>
<FieldRow>
<Controller
control={control}
name='bio'
rules={{ maxLength: { value: BIO_TEXT_MAX_LENGTH, message: t('Max_length_is', BIO_TEXT_MAX_LENGTH) } }}
render={({ field }) => (
<TextAreaInput
{...field}
id={bioId}
error={errors.bio?.message}
rows={3}
flexGrow={1}
addon={<Icon name='edit' size='x20' alignSelf='center' />}
aria-invalid={errors.statusText ? 'true' : 'false'}
aria-describedby={`${bioId}-error`}
/>
)}
rules={{ maxLength: BIO_TEXT_MAX_LENGTH }}
render={({ field }) => <TextAreaInput {...field} rows={3} />}
/>
</FieldRow>
{errors?.bio && (
<FieldError aria-live='assertive' id={`${bioId}-error`}>
{errors.bio.message}
</FieldError>
)}
</Field>

{/* Email */}
<Field>
<FieldLabel required htmlFor={emailId}>
{t('Email')}
</FieldLabel>
<FieldRow
display='flex'
flexDirection={isMobile ? 'column' : 'row'}
alignItems='stretch'
justifyContent='space-between'
className={css`
gap: 8px;
`}
>
<FieldRow>
<Controller
control={control}
name='email'
rules={{
required: t('Required_field', { field: t('Email') }),
validate: { validateEmail: (email) => (validateEmail(email) ? undefined : t('error-invalid-email-address')) },
}}
render={({ field }) => (
<TextInput
{...field}
id={emailId}
flexGrow={1}
error={errors.email?.message}
addon={<Icon name={isUserVerified ? 'circle-check' : 'mail'} size='x20' />}
disabled={!allowEmailChange}
aria-required='true'
aria-invalid={errors.email ? 'true' : 'false'}
aria-describedby={`${emailId}-error ${emailId}-hint`}
/>
)}
rules={{ validate: (email) => (validateEmail(email) ? undefined : t('error-invalid-email-address')) }}
render={({ field }) => <TextInput {...field} disabled={!allowEmailChange} />}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore input ids to keep labels associated.

FieldLabel htmlFor is set for nickname, bio, and email, but the inputs no longer receive the matching id, which breaks label association for assistive tech.

🛠️ Proposed fix
-<Controller control={control} name='nickname' render={({ field }) => <TextInput {...field} />} />
+<Controller control={control} name='nickname' render={({ field }) => <TextInput {...field} id={nicknameId} />} />
-	render={({ field }) => <TextAreaInput {...field} rows={3} />}
+	render={({ field }) => <TextAreaInput {...field} id={bioId} rows={3} />}
-	render={({ field }) => <TextInput {...field} disabled={!allowEmailChange} />}
+	render={({ field }) => <TextInput {...field} id={emailId} disabled={!allowEmailChange} />}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{/* Nickname */}
<Field>
<FieldLabel htmlFor={nicknameId}>{t('Nickname')}</FieldLabel>
<FieldRow>
<Controller
control={control}
name='nickname'
render={({ field }) => (
<TextInput {...field} id={nicknameId} flexGrow={1} addon={<Icon name='edit' size='x20' alignSelf='center' />} />
)}
/>
<Controller control={control} name='nickname' render={({ field }) => <TextInput {...field} />} />
</FieldRow>
</Field>
{/* Bio */}
<Field>
<FieldLabel htmlFor={bioId}>{t('Bio')}</FieldLabel>
<FieldRow>
<Controller
control={control}
name='bio'
rules={{ maxLength: { value: BIO_TEXT_MAX_LENGTH, message: t('Max_length_is', BIO_TEXT_MAX_LENGTH) } }}
render={({ field }) => (
<TextAreaInput
{...field}
id={bioId}
error={errors.bio?.message}
rows={3}
flexGrow={1}
addon={<Icon name='edit' size='x20' alignSelf='center' />}
aria-invalid={errors.statusText ? 'true' : 'false'}
aria-describedby={`${bioId}-error`}
/>
)}
rules={{ maxLength: BIO_TEXT_MAX_LENGTH }}
render={({ field }) => <TextAreaInput {...field} rows={3} />}
/>
</FieldRow>
{errors?.bio && (
<FieldError aria-live='assertive' id={`${bioId}-error`}>
{errors.bio.message}
</FieldError>
)}
</Field>
{/* Email */}
<Field>
<FieldLabel required htmlFor={emailId}>
{t('Email')}
</FieldLabel>
<FieldRow
display='flex'
flexDirection={isMobile ? 'column' : 'row'}
alignItems='stretch'
justifyContent='space-between'
className={css`
gap: 8px;
`}
>
<FieldRow>
<Controller
control={control}
name='email'
rules={{
required: t('Required_field', { field: t('Email') }),
validate: { validateEmail: (email) => (validateEmail(email) ? undefined : t('error-invalid-email-address')) },
}}
render={({ field }) => (
<TextInput
{...field}
id={emailId}
flexGrow={1}
error={errors.email?.message}
addon={<Icon name={isUserVerified ? 'circle-check' : 'mail'} size='x20' />}
disabled={!allowEmailChange}
aria-required='true'
aria-invalid={errors.email ? 'true' : 'false'}
aria-describedby={`${emailId}-error ${emailId}-hint`}
/>
)}
rules={{ validate: (email) => (validateEmail(email) ? undefined : t('error-invalid-email-address')) }}
render={({ field }) => <TextInput {...field} disabled={!allowEmailChange} />}
/>
{/* Nickname */}
<Field>
<FieldLabel htmlFor={nicknameId}>{t('Nickname')}</FieldLabel>
<FieldRow>
<Controller control={control} name='nickname' render={({ field }) => <TextInput {...field} id={nicknameId} />} />
</FieldRow>
</Field>
{/* Bio */}
<Field>
<FieldLabel htmlFor={bioId}>{t('Bio')}</FieldLabel>
<FieldRow>
<Controller
control={control}
name='bio'
rules={{ maxLength: BIO_TEXT_MAX_LENGTH }}
render={({ field }) => <TextAreaInput {...field} id={bioId} rows={3} />}
/>
</FieldRow>
</Field>
{/* Email */}
<Field>
<FieldLabel required htmlFor={emailId}>
{t('Email')}
</FieldLabel>
<FieldRow>
<Controller
control={control}
name='email'
rules={{ validate: (email) => (validateEmail(email) ? undefined : t('error-invalid-email-address')) }}
render={({ field }) => <TextInput {...field} id={emailId} disabled={!allowEmailChange} />}
/>
🤖 Prompt for AI Agents
In `@apps/meteor/client/views/account/profile/AccountProfileForm.tsx` around lines
238 - 270, The FieldLabel htmlFor values (nicknameId, bioId, emailId) were left
without matching input ids, breaking label association; update the Controller
renderers to pass the corresponding id prop to the inputs (e.g. in the nickname
Controller render pass id={nicknameId} to TextInput, in the bio Controller pass
id={bioId} to TextAreaInput, and in the email Controller pass id={emailId} to
TextInput while preserving disabled={!allowEmailChange} and existing validation
rules). Ensure you still spread the controller's field props ({...field}) so
value/onChange remain wired.

@Agarwalchetan
Copy link
Contributor

Hey!
I noticed your PR includes commits from previous branches — totally understandable when working fast.

In Rocket.Chat, opening each PR from develop helps keep the commit list minimal and makes reviews easier for maintainers.

Just wanted to share this in case it’s useful 👍

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 19 files

@KevLehman
Copy link
Member

We welcome contributions, but these are starting to get spammy. Please, read of contributing guidelines and code of conduct (available on the repo)

@KevLehman KevLehman closed this Jan 29, 2026
@sharanyamahajan
Copy link
Author

@KevLehman Understood, and apologies for this. I’ll carefully read and follow the contributing guidelines and the code of conduct before making any further contributions. Thank you for the reminder and for maintaining the standards of the repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants