Skip to content

Side Pane Stage 3: Specific help content for different types of reports #58847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4051ae9
Temporarily hide the help pane when centered modal is displayed
blazejkustra Mar 20, 2025
2402bf8
Implement ExpandableHelp
blazejkustra Mar 20, 2025
10aa442
Move content to separate files
blazejkustra Mar 20, 2025
88db295
Improve algorithm to show help content
blazejkustra Mar 20, 2025
ad26070
Fix scroll issue on native devices
blazejkustra Mar 20, 2025
8a8bb49
Implement HelpBulletList
blazejkustra Mar 20, 2025
fec00de
Rename ExpandableHelp
blazejkustra Mar 20, 2025
6ec3f41
Rename inbox to chat
blazejkustra Mar 20, 2025
6e32022
Add remaining content to search
blazejkustra Mar 20, 2025
a64d85d
Fix selecting text
blazejkustra Mar 20, 2025
818989a
Add userSelectNone style to HelpBulletList items
blazejkustra Mar 20, 2025
a666d0c
Update margin style for bullet list items in HelpBulletList component
blazejkustra Mar 20, 2025
baa32b3
Add containerStyle prop to HelpExpandable component for customizable …
blazejkustra Mar 20, 2025
12e9f81
Add missing content to Settings
blazejkustra Mar 20, 2025
c2517e3
Add missing content to Workspaces
blazejkustra Mar 20, 2025
ecc2ea3
Add missing content to Workspace
blazejkustra Mar 20, 2025
9c30165
Fix margin style for Reports head
blazejkustra Mar 20, 2025
e084f8d
Add default text for routes with no content
blazejkustra Mar 20, 2025
21ddd81
Fix lint
blazejkustra Mar 20, 2025
16f90ac
Rename Help components
blazejkustra Mar 20, 2025
9e0e1b3
Add HELP_TYPE constant to CONST
blazejkustra Mar 20, 2025
4bca2d1
Add getHelpPaneReportType function to determine help pane report type
blazejkustra Mar 20, 2025
5e7cae7
Add getExpenseType function to determine the expense type of a transa…
blazejkustra Mar 20, 2025
432bd36
Enhance substituteRouteParameters to support route parameter overrides
blazejkustra Mar 20, 2025
172cf09
Add tests for substituteRouteParameters to validate route parameter o…
blazejkustra Mar 20, 2025
d4bda5c
Refactor HelpContent to dynamically change :reportID into report types
blazejkustra Mar 20, 2025
0938d7b
Add help content mappings for various report types and expense catego…
blazejkustra Mar 20, 2025
8bccc1c
Move chat to index
blazejkustra Mar 20, 2025
7b83975
Add AdminsChatRoom component
blazejkustra Mar 20, 2025
5773223
Add Concierge component
blazejkustra Mar 20, 2025
b3c3600
Add WorkspaceChat component
blazejkustra Mar 20, 2025
9fa6727
Add ExpenseReportChat component
blazejkustra Mar 20, 2025
fbe6aee
Add ExpenseChat component
blazejkustra Mar 20, 2025
209d51f
Add ManualExpense component
blazejkustra Mar 20, 2025
530f9ee
Add ScanExpense component
blazejkustra Mar 20, 2025
5bb5bac
Add ExpensifyCardExpense component to HelpContent
blazejkustra Mar 20, 2025
ccdf003
Add ExpensifyCardPendingExpense
blazejkustra Mar 20, 2025
393df94
Add key prop to DiagnosticData component in HelpContent
blazejkustra Mar 21, 2025
e92f058
Add keyboard shortcut for DEBUG action in HelpModal
blazejkustra Mar 21, 2025
6137785
Fix onyx warning in an edge case
blazejkustra Mar 21, 2025
858d2a2
Update modal visibility logic in useSidePane hook to include CENTERED…
blazejkustra Mar 22, 2025
6e34704
Remove keyboard shortcut for DEBUG action in HelpModal
blazejkustra Mar 22, 2025
6c24bdd
Merge branch 'main' into side-pane/stage-3
blazejkustra Mar 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,16 @@ const CONST = {
BILL: 'bill',
},
CHAT_TYPE: chatTypes,
HELP_TYPE: {
...chatTypes,
CHAT_CONCIERGE: 'concierge',
EXPENSE_REPORT: 'expenseReport',
EXPENSE: 'expense',
CHAT: 'chat',
IOU: 'iou',
TASK: 'task',
INVOICE: 'invoice',
},
WORKSPACE_CHAT_ROOMS: {
ANNOUNCE: '#announce',
ADMINS: '#admins',
Expand Down Expand Up @@ -2659,6 +2669,14 @@ const CONST = {
SCAN: 'scan',
PER_DIEM: 'per-diem',
},
EXPENSE_TYPE: {
DISTANCE: 'distance',
MANUAL: 'manual',
SCAN: 'scan',
PER_DIEM: 'per-diem',
EXPENSIFY_CARD: 'expensifyCard',
PENDING_EXPENSIFY_CARD: 'pendingExpensifyCard',
},
REPORT_ACTION_TYPE: {
PAY: 'pay',
CREATE: 'create',
Expand Down
2 changes: 1 addition & 1 deletion src/components/HeaderWithBackButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as Expensicons from '@components/Icon/Expensicons';
import PinButton from '@components/PinButton';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import SearchButton from '@components/Search/SearchRouter/SearchButton';
import HelpButton from '@components/SidePane/HelpButton';
import HelpButton from '@components/SidePane/HelpComponents/HelpButton';
import ThreeDotsMenu from '@components/ThreeDotsMenu';
import Tooltip from '@components/Tooltip';
import useLocalize from '@hooks/useLocalize';
Expand Down
2 changes: 1 addition & 1 deletion src/components/Navigation/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Breadcrumbs from '@components/Breadcrumbs';
import LoadingBar from '@components/LoadingBar';
import {PressableWithoutFeedback} from '@components/Pressable';
import SearchButton from '@components/Search/SearchRouter/SearchButton';
import HelpButton from '@components/SidePane/HelpButton';
import HelpButton from '@components/SidePane/HelpComponents/HelpButton';
import Text from '@components/Text';
import WorkspaceSwitcherButton from '@components/WorkspaceSwitcherButton';
import useLocalize from '@hooks/useLocalize';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {SearchQueryJSON, SearchQueryString} from '@components/Search/types'
import {isSearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem';
import type {SearchQueryItem} from '@components/SelectionList/Search/SearchQueryListItem';
import type {SelectionListHandle} from '@components/SelectionList/types';
import HelpButton from '@components/SidePane/HelpButton';
import HelpButton from '@components/SidePane/HelpComponents/HelpButton';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down
63 changes: 0 additions & 63 deletions src/components/SidePane/Help/HelpContent.tsx

This file was deleted.

26 changes: 26 additions & 0 deletions src/components/SidePane/HelpComponents/HelpBulletList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type {ReactNode} from 'react';
import React from 'react';
import {View} from 'react-native';
import Text from '@components/Text';
import type {ThemeStyles} from '@styles/index';
import CONST from '@src/CONST';

type HelpBulletListProps = {
styles: ThemeStyles;
items: ReactNode[];
};

function HelpBulletList({items, styles}: HelpBulletListProps) {
return items.map((item, index) => (
<View
// eslint-disable-next-line react/no-array-index-key
key={`bullet-list-item-${index}`}
style={[styles.flexRow, styles.alignItemsStart, styles.mt3]}
>
<Text style={[styles.textNormal, styles.pr2, styles.userSelectNone]}>{CONST.DOT_SEPARATOR}</Text>
<View style={[styles.flex1]}>{item}</View>
</View>
));
}

export default HelpBulletList;
89 changes: 89 additions & 0 deletions src/components/SidePane/HelpComponents/HelpContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {findFocusedRoute} from '@react-navigation/native';
import React, {useEffect, useMemo, useRef} from 'react';
// Importing from the react-native-gesture-handler package instead of the `components/ScrollView` to fix scroll issue:
// https://github.com/react-native-modal/react-native-modal/issues/236
import {ScrollView} from 'react-native-gesture-handler';
import {useOnyx} from 'react-native-onyx';
import HeaderGap from '@components/HeaderGap';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import getHelpContent from '@components/SidePane/HelpContent/getHelpContent';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useRootNavigationState from '@hooks/useRootNavigationState';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
import {getHelpPaneReportType} from '@libs/ReportUtils';
import {substituteRouteParameters} from '@libs/SidePaneUtils';
import {getExpenseType} from '@libs/TransactionUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';

type HelpContentProps = {
closeSidePane: (shouldUpdateNarrow?: boolean) => void;
};

function HelpContent({closeSidePane}: HelpContentProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isProduction} = useEnvironment();
const {isExtraLargeScreenWidth} = useResponsiveLayout();

const routeParams = useRootNavigationState((state) => (findFocusedRoute(state)?.params as Record<string, string>) ?? {});
const reportID = routeParams.reportID || CONST.DEFAULT_NUMBER_ID;
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [parentReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report?.parentReportID || CONST.DEFAULT_NUMBER_ID}`, {
canEvict: false,
});
const parentReportAction = report?.parentReportActionID ? parentReportActions?.[report.parentReportActionID] : undefined;
const linkedTransactionID = useMemo(() => (isMoneyRequestAction(parentReportAction) ? getOriginalMessage(parentReportAction)?.IOUTransactionID : undefined), [parentReportAction]);
const [transaction] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${linkedTransactionID ?? CONST.DEFAULT_NUMBER_ID}`);

const route = useMemo(() => {
const expenseType = getExpenseType(transaction);
const overrides = {reportID: expenseType ? `:${CONST.REPORT.HELP_TYPE.EXPENSE}/:${expenseType}` : `:${getHelpPaneReportType(report)}`};
const activeRoute = Navigation.getActiveRouteWithoutParams();
return substituteRouteParameters(activeRoute, routeParams, overrides);
}, [transaction, report, routeParams]);

const wasPreviousNarrowScreen = useRef(!isExtraLargeScreenWidth);
useEffect(() => {
// Close the side pane when the screen size changes from large to small
if (!isExtraLargeScreenWidth && !wasPreviousNarrowScreen.current) {
closeSidePane(true);
wasPreviousNarrowScreen.current = true;
}

// Reset the trigger when the screen size changes back to large
if (isExtraLargeScreenWidth) {
wasPreviousNarrowScreen.current = false;
}
}, [isExtraLargeScreenWidth, closeSidePane]);

return (
<>
<HeaderGap />
<HeaderWithBackButton
title={translate('common.help')}
style={styles.headerBarDesktopHeight}
onBackButtonPress={() => closeSidePane(false)}
onCloseButtonPress={() => closeSidePane(false)}
shouldShowBackButton={!isExtraLargeScreenWidth}
shouldShowCloseButton={isExtraLargeScreenWidth}
shouldDisplayHelpButton={false}
/>
<ScrollView
style={[styles.ph5, styles.pb5]}
userSelect="auto"
>
{getHelpContent(styles, route, isProduction)}
</ScrollView>
</>
);
}

HelpContent.displayName = 'HelpContent';

export default HelpContent;
38 changes: 38 additions & 0 deletions src/components/SidePane/HelpComponents/HelpExpandable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, {useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import Text from '@components/Text';
import type {ThemeStyles} from '@styles/index';

type HelpExpandableProps = {
children: React.ReactNode;
styles: ThemeStyles;
containerStyle?: StyleProp<ViewStyle>;
title?: string;
moreText?: string;
};

function HelpExpandable({children, styles, containerStyle, title, moreText = '(more)'}: HelpExpandableProps) {
const [isExpanded, setIsExpanded] = useState(false);

return (
<View style={containerStyle}>
<Text style={styles.textNormal}>
{title}{' '}
{!isExpanded && (
<Text
style={styles.link}
onPress={() => setIsExpanded(true)}
>
{moreText}
</Text>
)}
</Text>
{isExpanded && children}
</View>
);
}

HelpExpandable.displayName = 'ExpandableHelp';

export default HelpExpandable;
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';

type SidePaneOverlayProps = {
type HelpOverlayProps = {
/** Whether the side pane is displayed over RHP */
isRHPVisible: boolean;

Expand All @@ -15,7 +15,7 @@ type SidePaneOverlayProps = {

const easing = Easing.bezier(0.76, 0.0, 0.24, 1.0).factory();

function SidePaneOverlay({isRHPVisible, onBackdropPress}: SidePaneOverlayProps) {
function HelpOverlay({isRHPVisible, onBackdropPress}: HelpOverlayProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();

Expand Down Expand Up @@ -51,6 +51,6 @@ function SidePaneOverlay({isRHPVisible, onBackdropPress}: SidePaneOverlayProps)
);
}

SidePaneOverlay.displayName = 'SidePaneOverlay';
HelpOverlay.displayName = 'HelpOverlay';

export default SidePaneOverlay;
export default HelpOverlay;
39 changes: 39 additions & 0 deletions src/components/SidePane/HelpContent/chat/admins.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint-disable react/no-unescaped-entities */
import React from 'react';
import BulletList from '@components/SidePane/HelpComponents/HelpBulletList';
import ExpandableHelp from '@components/SidePane/HelpComponents/HelpExpandable';
import Text from '@components/Text';
import type {ThemeStyles} from '@styles/index';

function AdminsChatRoom({styles}: {styles: ThemeStyles}) {
return (
<>
<Text style={[styles.textHeadlineH1, styles.mb4]}>#admins</Text>
<ExpandableHelp
styles={styles}
title="Every workspace automatically receives a special #admins chat room. Every admin is automatically added to this room as a member. The #admins room is used for several purposes:"
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, is it normal to have all these copies here instead of using the translation keys as everywhere else? The same applies to the other files. Not sure if we are planning to do it later

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The side pane is only displayed when English is selected in the settings, so we decided to hardcode the text in these components.

(Eventually everything will be moved to markdown files so it doesn't matter)

Copy link
Contributor

Choose a reason for hiding this comment

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

>
<BulletList
styles={styles}
items={[
<Text style={styles.textNormal}>
<Text style={styles.textBold}>Talking with Concierge, your setup specialist, or your account manager</Text> - When you first create the workspace, Concierge and a
setup specialist will be added. Feel free to ask any setup questions you have about how to configure the workspace, onboard your team, connect your accounting, or
anything else you might need.
</Text>,
<Text style={styles.textNormal}>
<Text style={styles.textBold}>Monitoring workspace changes</Text> - Every #admins room shows an audit trail of any configuration changes or significant events
happening inside the workspace.
</Text>,
<Text style={styles.textNormal}>
<Text style={styles.textBold}>Chatting with other admins</Text> - The #admins room is a useful space for workspace admins to chat with each other about anything,
whether or not it relates to Expensify.
</Text>,
]}
/>
</ExpandableHelp>
</>
);
}

export default AdminsChatRoom;
18 changes: 18 additions & 0 deletions src/components/SidePane/HelpContent/chat/concierge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* eslint-disable react/no-unescaped-entities */
import React from 'react';
import Text from '@components/Text';
import type {ThemeStyles} from '@styles/index';

function Concierge({styles}: {styles: ThemeStyles}) {
return (
<>
<Text style={[styles.textHeadlineH1, styles.mb4]}>Concierge</Text>
<Text style={styles.textNormal}>
Concierge is available 24/7 to answer any question you have about anything, whether that's how to get set up, how to fix a problem, or general best practices. Concierge is a
bot, but is really smart, and can escalate you to a human whenever you want. Say hi, it's friendly!
</Text>
</>
);
}

export default Concierge;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable react/no-unescaped-entities */
import React from 'react';
import Text from '@components/Text';
import type {ThemeStyles} from '@styles/index';

function ExpensifyCardExpense({styles}: {styles: ThemeStyles}) {
return (
<>
<Text style={[styles.textHeadlineH1, styles.mb4]}>Expensify Card</Text>
<Text style={styles.textNormal}>An "Expensify Card" expense corresponds to a "posted" (meaning, finalized by the bank) purchase.</Text>
<Text style={[styles.textNormal, styles.pt3]}>Expensify Card expenses cannot be reimbursed as they are centrally paid by the bank account linked to the workspace.</Text>
</>
);
}

export default ExpensifyCardExpense;
Loading
Loading