[No QA] Fix sign-in page heading hierarchy for WCAG 1.3.1 compliance#84834
[No QA] Fix sign-in page heading hierarchy for WCAG 1.3.1 compliance#84834
Conversation
The sign-in page had 6 elements all rendering as h1 (via
accessibilityRole="header" with no aria-level). Per WCAG 1.3.1
there should be a single h1 per page. This adds aria-level={2}
to the footer column titles and the welcome header, keeping only
the hero text as the page's h1.
Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
|
The failing check is unrelated to this PR. Analysis: The failing test is Evidence: This PR only modifies two sign-in page layout files:
The failing test ( |
|
@MelvinBot merge main and verify if the fix is correct |
…eadingHierarchy Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
|
Merged Merge: Clean merge with no conflicts — the PR's changes in Fix verification: There are exactly 3 elements with
This is the correct heading hierarchy per WCAG 1.3.1 — a single |
…Utils.ts Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
|
Fixed the failing typecheck: Note: This is a pre-existing issue on |
|
Fixed the failing checks: The All checks are now passing (typecheck, all 8 test jobs, ESLint, builds, storybook). |
|
@MelvinBot yo don't do unrelated changes. remove it. Merge main, and mark ready for review
|
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / Safari |
Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
Co-authored-by: Rushat Gabhane <rushatgabhane@users.noreply.github.com>
Concierge reviewer checklist:
For more detailed instructions on completing this checklist, see How do I review a HelpDot PR as a Concierge Team member? |
|
@brunovjk @CortneyOfstad One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
Done. Here's what I did:
|
|
A preview of your ExpensifyHelp changes have been deployed to https://c26eec69.helpdot.pages.dev ⚡️ Updated articles: |
| ); | ||
|
|
||
| const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, { | ||
| const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, { |
There was a problem hiding this comment.
❌ PERF-11 (docs)
The useOnyx call for parentReportActions subscribes to the entire report actions object for this report without a selector, but the component only needs a single action (parentReportActions?.[transactionThreadReport.parentReportActionID]). The previous code used a selector that returned just the one needed action, which meant Onyx's deepEqual ran on a single small object and skipped re-renders when unrelated actions changed. Without the selector, any change to any report action in this report will trigger a re-render of this component.
Restore the selector to pick only the needed report action:
const parentReportActionID = transactionThreadReport?.parentReportActionID;
const parentReportActionSelector = useCallback(
(reportActions: OnyxEntry<OnyxTypes.ReportActions>): OnyxEntry<OnyxTypes.ReportAction> => reportActions?.[`${parentReportActionID}`],
[parentReportActionID],
);
const [parentReportAction] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${parentReportID}`, {
canEvict: false,
selector: parentReportActionSelector,
});Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
| sourceTransactionPolicy, | ||
| ); | ||
|
|
||
| setMergeTransactionKey(transactionID, mergeableData); |
There was a problem hiding this comment.
❌ PERF-6 (docs)
setMergeTransactionKey(transactionID, mergeableData) is a side effect (Onyx write via Onyx.merge) placed inside a useMemo. useMemo must be a pure computation with no side effects -- React may re-run it at any time, and in Strict Mode it runs twice during development. Deriving conflictFields via useMemo is correct, but the Onyx write should be moved to a separate useEffect that reacts to the computed data.
Extract the side effect into a useEffect:
const {detectedConflictFields, mergeableData} = useMemo(() => {
if (!transactionID || !targetTransaction || !sourceTransaction) {
return {detectedConflictFields: [], mergeableData: {}};
}
return getMergeableDataAndConflictFields(
targetTransaction, sourceTransaction, localeCompare,
[targetTransactionReport, sourceTransactionReport],
targetTransactionPolicy, sourceTransactionPolicy,
);
}, [targetTransaction, sourceTransaction, transactionID, localeCompare, sourceTransactionReport, targetTransactionReport, targetTransactionPolicy, sourceTransactionPolicy]);
const conflictFields = detectedConflictFields as MergeFieldKey[];
useEffect(() => {
if (transactionID && Object.keys(mergeableData).length > 0) {
setMergeTransactionKey(transactionID, mergeableData);
}
}, [transactionID, mergeableData]);Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
| - **Reimbursable** to update whether the expense is reimbursable or non-reimbursable. | ||
| - **Billable** to update whether the expense is billable or non-billable (Billable must be enabled on the workspace). | ||
| 8. Select whether the rule should be applied to existing unsubmitted expenses and preview matches (optional). | ||
| 9. Select **Save Rule** |
There was a problem hiding this comment.
Readability violation -- Duplicate step and broken numbered list: Step 9 appears twice (here on line 35 without a period, and again on line 39 with a period). The Note on line 37 is sandwiched between them, breaking the numbered list flow. This will confuse readers.
Suggested fix: Remove this first "9. Select Save Rule" (line 35), and move the Note block to after step 9 on line 39, so the numbered list flows cleanly from 1-9 without interruption.
|
|
||
| **Note:** Workspace Merchant Rules are only available after **Workspace Rules** are enabled for the workspace. If you don't see the Merchant Rules section, first [enable Workspace Rules](https://help.expensify.com/articles/new-expensify/workspaces/Workspace-Rules#enable-workspace-rules). | ||
|
|
||
| 9. Select **Save Rule**. |
There was a problem hiding this comment.
Readability violation -- Duplicate step 9: This is the second occurrence of "9. Select Save Rule." The first appears on line 35. One of these should be removed. Keep this one (with proper punctuation) and delete line 35.
|
|
||
| --- | ||
|
|
||
| ## When Workspace Merchant Rules apply to expenses |
There was a problem hiding this comment.
AI Readiness violation -- Incorrect heading level: This ## heading is not logically a subsection of "# How to create Workspace Merchant Rules" above. "When Workspace Merchant Rules apply to expenses" is an independent top-level topic. It should use # (H1) instead of ## (H2).
The same applies to "## How to use Workspace Merchant Rules" (line 53) and "## Examples of Workspace Merchant Rules" (line 69) -- all three should be promoted to # headings since they are standalone sections, not substeps of the creation instructions.
|
|
||
| --- | ||
|
|
||
| ## How to use Workspace Merchant Rules |
There was a problem hiding this comment.
AI Readiness violation -- Incorrect heading level: This should be # (H1), not ## (H2). This is an independent section about best practices, not a subsection of the creation steps above. Using ## here creates a misleading document hierarchy for both readers and AI systems parsing the content.
|
|
||
| --- | ||
|
|
||
| ## Examples of Workspace Merchant Rules |
There was a problem hiding this comment.
AI Readiness violation -- Incorrect heading level: This should be # (H1), not ## (H2). "Examples of Workspace Merchant Rules" is a standalone section, not a subsection of the best practices section above.
| internalScope: Audience is Workspace Admins on the Control plan. Covers enabling and using Workspace Merchant Rules to apply consistent expense coding based on merchant name. Does not cover personal expense rules, Category Rules, Tag Rules, or troubleshooting rule conflicts. | ||
| --- | ||
|
|
||
| Enable Workspace Merchant Rules to help Workspace Admins standardize how expenses from common merchants are coded across the workspace. These rules automatically apply consistent categories, tags, and other expense fields based on the merchant name, reducing manual cleanup and improving reporting accuracy. |
There was a problem hiding this comment.
Readability violation -- Intro paragraph starts with a command: The opening sentence "Enable Workspace Merchant Rules to help Workspace Admins..." reads like an instruction rather than a feature description. The introduction should explain what the feature is before telling users to enable it.
Suggested fix: Rewrite to lead with a description, e.g.:
"Workspace Merchant Rules help Workspace Admins standardize how expenses from common merchants are coded across the workspace. These rules automatically apply consistent categories, tags, and other expense fields based on the merchant name, reducing manual cleanup and improving reporting accuracy."
|
|
||
| Enable Workspace Merchant Rules to help Workspace Admins standardize how expenses from common merchants are coded across the workspace. These rules automatically apply consistent categories, tags, and other expense fields based on the merchant name, reducing manual cleanup and improving reporting accuracy. | ||
|
|
||
| These rules work in New Expensify, can only be configured in New Expensify, and apply consistently across all workspace members. |
There was a problem hiding this comment.
Readability violation -- Redundant sentence: "These rules work in New Expensify, can only be configured in New Expensify, and apply consistently across all workspace members." This sentence says "New Expensify" twice in very close succession. It also repeats information from line 8 ("apply consistently across all workspace members" echoes "coded across the workspace").
Suggested fix: Consolidate into one sentence, e.g.: "These rules can only be configured in New Expensify and apply consistently across all workspace members."
| - **eReceipts** – Enable automatic receipt generation for all USD card transactions up to $75 (requires USD as default currency). | ||
| - **Merchant-based automation** – Automatically apply categories, tags, and other fields using Workspace Merchant Rules. | ||
|
|
||
| {:width="100%"} |
There was a problem hiding this comment.
AI Readiness violation -- Missing alt text on image: This image has empty alt text . All images should include descriptive alt text for accessibility and AI readiness, e.g.:

Additionally, this image filename (ExpensifyHelp-FlagExpensesMissingItemizedReceipts_01.png) seems to be about itemized receipts, not about merchant-based automation. Please verify this is the intended screenshot for the Merchant Rules section context.
| - **Billable default** – Choose whether expenses are billable by default. Note: [Workspace Merchant Rules](https://help.expensify.com/articles/new-expensify/workspaces/Workspace-Merchant-Rules) can also set billable status on a per-merchant basis. | ||
| - **Require company cards for all purchases** - Flag out-of-pocket expenses that should have been made with a company card. Only available after company cards are connected to the workspace. | ||
| - **eReceipts** – Enable automatic receipt generation for all USD card transactions up to $75 (requires USD as default currency). | ||
| - **Merchant-based automation** – Automatically apply categories, tags, and other fields using Workspace Merchant Rules. |
There was a problem hiding this comment.
Readability violation -- Vague bullet point: "Automatically apply categories, tags, and other fields using Workspace Merchant Rules" -- this bullet uses the phrase "other fields" which is vague. Other bullets in this list are specific about what they control. Consider listing the specific fields (e.g., "categories, tags, descriptions, tax, reimbursable status, and billable status") or linking directly to the Workspace Merchant Rules article for the full list.
|
|
||
| --- | ||
|
|
||
| # How to configure Workspace Merchant Rules |
There was a problem hiding this comment.
Readability violation -- Redundant section: This section largely duplicates the content of the dedicated Workspace Merchant Rules article. The three bullet points (apply when created, workspace level, don't change submission) repeat what is already in the linked article. Since this section ends with a "see:" link to the full article, consider trimming this to a shorter cross-reference -- a single sentence with the link would suffice and avoid content drift as the main article evolves.
For example:
# Workspace Merchant Rules
Workspace Merchant Rules let Workspace Admins automatically apply consistent coding to expenses based on the merchant name. For setup instructions and details, see [**Workspace Merchant Rules**](https://help.expensify.com/articles/new-expensify/workspaces/Workspace-Merchant-Rules).
|
|
||
| You can create an expense by scanning a receipt, entering details manually, or tracking distance. The steps below walk you through each method so you can quickly capture and submit expenses for reimbursement or record-keeping. | ||
|
|
||
| **Note:** When creating an expense, Workspace Merchant Rules set by your workspace may automatically apply categories, tags, descriptions, reimbursable status, billable status, and other fields based on the merchant name. |
There was a problem hiding this comment.
Readability violation -- Audience mismatch and verbose phrasing: This article targets expense submitters, not Workspace Admins. The phrase "Workspace Merchant Rules set by your workspace" is slightly awkward ("set by your workspace" is imprecise -- rules are set by Workspace Admins, not by the workspace itself). Also, listing every possible field ("categories, tags, descriptions, reimbursable status, billable status, and other fields") is verbose for a Note aimed at submitters who cannot configure these rules.
Suggested fix: Keep it concise and link to the full article:
"Note: When you create an expense, Workspace Merchant Rules configured by your Workspace Admin may automatically fill in certain fields based on the merchant name."
| ## What happens after submitting an expense to a workspace | ||
|
|
||
| - Expenses are automatically added to a report. | ||
| - [Workspace Merchant Rules](https://help.expensify.com/articles/new-expensify/workspaces/Workspace-Merchant-Rules) may automatically apply categories, tags, descriptions, and other coding based on the merchant name. |
There was a problem hiding this comment.
Readability violation -- Incorrect placement in sequential list: This bullet about Workspace Merchant Rules applying categories is placed after "Expenses are automatically added to a report" and before "Workspace rules check for violations." However, Workspace Merchant Rules apply at expense creation time (as stated in the main article), not after submission. Placing this in the "What happens after submitting" list implies these rules trigger on submission, which is inaccurate.
Consider moving this information to the Note on line 26 instead, or adding a clarifying qualifier like "may have already applied..." to indicate this happened at creation, not submission.
| 8. Select whether the rule should be applied to existing unsubmitted expenses and preview matches (optional). | ||
| 9. Select **Save Rule** | ||
|
|
||
| **Note:** Workspace Merchant Rules are only available after **Workspace Rules** are enabled for the workspace. If you don't see the Merchant Rules section, first [enable Workspace Rules](https://help.expensify.com/articles/new-expensify/workspaces/Workspace-Rules#enable-workspace-rules). |
There was a problem hiding this comment.
Readability violation -- Misplaced prerequisite note: This important prerequisite ("Workspace Merchant Rules are only available after Workspace Rules are enabled") is buried after the step-by-step instructions. Readers who don't have Rules enabled will follow all 8 steps before learning they need to enable Rules first.
Suggested fix: Move this Note to before the numbered steps (e.g., right after "To create a Workspace Merchant Rule:" on line 16), so readers know the prerequisite upfront.
| - The rule is disabled. | ||
| - Another rule matched first. | ||
| - A field was manually set during expense creation. | ||
| - The rule was created after the expense and wasn't applied retroactively, unless you select "apply to existing expenses" when creating the rule. |
There was a problem hiding this comment.
Readability violation -- Missing final newline: The file does not end with a trailing newline. Most Markdown style guides and linters expect files to end with a newline character. Add a blank line at the end of the file.
HelpDot Documentation ReviewOverall AssessmentThis PR introduces documentation for a new Workspace Merchant Rules feature across five files: one new standalone article and four existing articles updated with cross-references and contextual notes. The writing is generally clear, well-structured, and follows Expensify documentation conventions. The new article is comprehensive with practical examples and a solid FAQ. However, there are a few notable issues -- a duplicate step in the main article's numbered list and an inconsistent heading hierarchy -- that should be resolved before merging. Scores Summary
Key FindingsIssues that should be addressed:
Positive aspects:
Recommendations
Files Reviewed
Note: This review covers only the proposed documentation changes in the PR diff, not unchanged content. |
|
The failing check ( Failing test: Error: Root cause: The mock file imports Origin: Both the mock file and the test were newly added to Evidence: This PR only modifies Fix needed on main: In import {PERMISSIONS, RESULTS} from 'react-native-permissions/dist/commonjs/permissions';to: import {PERMISSIONS} from 'react-native-permissions/dist/commonjs/permissions';
import {RESULTS} from 'react-native-permissions/dist/commonjs/results'; |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: baaf0426f2
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| <Animated.View style={[styles.pAbsolute, styles.r0, styles.h100, styles.overflowHidden, animatedWidthStyle]}> | ||
| <DialogLabelProvider> | ||
| <RHPDialogContainer> | ||
| <Stack.Navigator | ||
| screenOptions={screenOptions} | ||
| screenListeners={screenListeners} | ||
| id={NAVIGATORS.RIGHT_MODAL_NAVIGATOR} | ||
| > | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_ROUTER} | ||
| component={ModalStackNavigators.SearchRouterModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SETTINGS} | ||
| component={ModalStackNavigators.SettingsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.TWO_FACTOR_AUTH} | ||
| component={ModalStackNavigators.TwoFactorAuthenticatorStackNavigator} | ||
| listeners={{ | ||
| beforeRemove: () => { | ||
| // eslint-disable-next-line @typescript-eslint/no-deprecated | ||
| InteractionManager.runAfterInteractions(() => clearTwoFactorAuthData(true)); | ||
| }, | ||
| }} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.NEW_CHAT} | ||
| component={ModalStackNavigators.NewChatModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.PROFILE} | ||
| component={ModalStackNavigators.ProfileModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.DEBUG} | ||
| component={ModalStackNavigators.DebugModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.NEW_REPORT_WORKSPACE_SELECTION} | ||
| component={ModalStackNavigators.NewReportWorkspaceSelectionModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_DETAILS} | ||
| component={ModalStackNavigators.ReportDetailsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_CARD_ACTIVATE} | ||
| component={ModalStackNavigators.ReportCardActivateStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_CHANGE_WORKSPACE} | ||
| component={ModalStackNavigators.ReportChangeWorkspaceModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_CHANGE_APPROVER} | ||
| component={ModalStackNavigators.ReportChangeApproverModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_SETTINGS} | ||
| component={ModalStackNavigators.ReportSettingsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_DESCRIPTION} | ||
| component={ModalStackNavigators.ReportDescriptionModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REPORT_VERIFY_ACCOUNT} | ||
| component={ModalStackNavigators.ReportVerifyAccountModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SETTINGS_CATEGORIES} | ||
| component={ModalStackNavigators.CategoriesModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SETTINGS_TAGS} | ||
| component={ModalStackNavigators.TagsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.EXPENSIFY_CARD} | ||
| component={ModalStackNavigators.ExpensifyCardModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.DOMAIN_CARD} | ||
| component={ModalStackNavigators.DomainCardModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.PARTICIPANTS} | ||
| component={ModalStackNavigators.ReportParticipantsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.ROOM_MEMBERS} | ||
| component={ModalStackNavigators.RoomMembersModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.MONEY_REQUEST} | ||
| component={ModalStackNavigators.MoneyRequestModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.WORKSPACE_CONFIRMATION} | ||
| component={ModalStackNavigators.WorkspaceConfirmationModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.WORKSPACE_DUPLICATE} | ||
| component={ModalStackNavigators.WorkspaceDuplicateModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.NEW_TASK} | ||
| component={ModalStackNavigators.NewTaskModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.TEACHERS_UNITE} | ||
| component={ModalStackNavigators.NewTeachersUniteNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.TASK_DETAILS} | ||
| component={ModalStackNavigators.TaskModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.ENABLE_PAYMENTS} | ||
| component={ModalStackNavigators.EnablePaymentsStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SPLIT_DETAILS} | ||
| component={ModalStackNavigators.SplitDetailsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.ADD_PERSONAL_BANK_ACCOUNT} | ||
| component={ModalStackNavigators.AddPersonalBankAccountModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.WALLET_STATEMENT} | ||
| component={ModalStackNavigators.WalletStatementStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.FLAG_COMMENT} | ||
| component={ModalStackNavigators.FlagCommentStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.EDIT_REQUEST} | ||
| component={ModalStackNavigators.EditRequestStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SIGN_IN} | ||
| component={ModalStackNavigators.SignInModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.REFERRAL} | ||
| component={ModalStackNavigators.ReferralModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.PRIVATE_NOTES} | ||
| component={ModalStackNavigators.PrivateNotesModalStackNavigator} | ||
| options={hideKeyboardOnSwipe} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.TRANSACTION_DUPLICATE} | ||
| component={ModalStackNavigators.TransactionDuplicateStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.MERGE_TRANSACTION} | ||
| component={ModalStackNavigators.MergeTransactionStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.TRAVEL} | ||
| component={ModalStackNavigators.TravelModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_REPORT_ACTIONS} | ||
| component={ModalStackNavigators.SearchReportActionsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.RESTRICTED_ACTION} | ||
| component={ModalStackNavigators.RestrictedActionModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_ADVANCED_FILTERS} | ||
| component={ModalStackNavigators.SearchAdvancedFiltersModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_SAVED_SEARCH} | ||
| component={ModalStackNavigators.SearchSavedSearchModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.MISSING_PERSONAL_DETAILS} | ||
| component={ModalStackNavigators.MissingPersonalDetailsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.ADD_UNREPORTED_EXPENSE} | ||
| component={ModalStackNavigators.AddUnreportedExpenseModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SCHEDULE_CALL} | ||
| component={ModalStackNavigators.ScheduleCallModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_REPORT} | ||
| getComponent={loadRHPReportScreen} | ||
| options={(props) => { | ||
| const options = modalStackScreenOptions(props); | ||
| return {...options, animation: animationEnabledOnSearchReport ? Animations.SLIDE_FROM_RIGHT : Animations.NONE}; | ||
| }} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.EXPENSE_REPORT} | ||
| getComponent={loadSearchMoneyRequestReportPage} | ||
| options={(props) => { | ||
| const options = modalStackScreenOptions(props); | ||
| return {...options, animation: isSmallScreenWidth ? Animations.SLIDE_FROM_RIGHT : Animations.NONE}; | ||
| }} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_MONEY_REQUEST_REPORT} | ||
| getComponent={loadSearchMoneyRequestReportPage} | ||
| options={(props) => { | ||
| const options = modalStackScreenOptions(props); | ||
| return {...options, animation: isSmallScreenWidth ? Animations.SLIDE_FROM_RIGHT : Animations.NONE}; | ||
| }} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.DOMAIN} | ||
| component={ModalStackNavigators.WorkspacesDomainModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.SEARCH_COLUMNS} | ||
| component={ModalStackNavigators.SearchColumnsModalStackNavigator} | ||
| /> | ||
| <Stack.Screen | ||
| name={SCREENS.RIGHT_MODAL.MULTIFACTOR_AUTHENTICATION} | ||
| component={ModalStackNavigators.MultifactorAuthenticationStackNavigator} | ||
| /> | ||
| </Stack.Navigator> | ||
| </RHPDialogContainer> | ||
| </DialogLabelProvider> | ||
| <Stack.Navigator | ||
| screenOptions={screenOptions} | ||
| screenListeners={screenListeners} | ||
| id={NAVIGATORS.RIGHT_MODAL_NAVIGATOR} |
There was a problem hiding this comment.
Restore dialog semantics for desktop right-hand modals
Rendering the Stack.Navigator directly here removes the role="dialog", aria-modal, and label wiring that previously came from RHPDialogContainer, so screen readers on wide layouts no longer get a proper modal boundary/title announcement for RHP screens. I checked src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx:152-157, and it only applies dialog semantics when isSmallScreenWidth is true, so desktop/web RHP modals are now unlabeled non-dialog containers.
Useful? React with 👍 / 👎.
| setClipboardMessage( | ||
| contentWithoutFollowups.replaceAll(/(<mention-user>)(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => { | ||
| content.replaceAll(/(<mention-user>)(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => { | ||
| const modifiedContent = Str.removeSMSDomain(innerContent) || ''; |
There was a problem hiding this comment.
Strip followup payload before copying message content
This path now copies raw content instead of removing <followup-list> metadata first, so copying Concierge/system messages can include hidden suggested-followup text/markup that is not part of the visible message body. Users pasting copied content will get extra unintended text; the previous stripFollowupListFromHtml(...) step should be preserved before calling setClipboardMessage.
Useful? React with 👍 / 👎.
Explanation of Change
The sign-in page (new.expensify.com) had 6 elements all rendering as
<h1>headings becauseaccessibilityRole="header"with noaria-leveldefaults to heading level 1 in react-native-web. Per WCAG 1.3.1, there should be a single h1 per page to maintain proper heading hierarchy.This adds
aria-level={2}to the footer column titles (Features, Resources, Learn More, Get Started) inFooter.tsxand the welcome header (e.g., "Get started below.") inSignInPageContent.tsx, keeping only the hero text ("Travel and expense, at the speed of chat") as the page's h1.Fixed Issues
$ #76945
Tests
new.expensify.comin Chromedocument.querySelectorAll('[role="heading"]').forEach(el => console.log('h' + (el.getAttribute('aria-level') || '?') + ': ' + el.textContent.trim().substring(0, 60)))<h1>element)Offline tests
N/A — This is a purely semantic accessibility change that does not involve network requests.
QA Steps
HhotkeyPR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Web: Chrome
Tested on web — hero text renders as h1, footer section titles and welcome header render as h2. No visual changes.