Skip to content

Commit f89bee0

Browse files
committed
merge latest main
2 parents b248f43 + c90e42d commit f89bee0

261 files changed

Lines changed: 7074 additions & 1599 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/coding-standards/rules/consistency-5-justify-eslint-disable.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ title: Justify ESLint rule disables
77

88
### Reasoning
99

10-
ESLint rule disables without justification can mask underlying issues and reduce code quality. Clear documentation ensures team members understand exceptions, promoting better maintainability.
10+
`eslint-disable` comments completely hide violations from the seatbelt baseline — they will never be fixed unless the comment is removed. Because of this, they should only be used for **permanent** suppressions where you are certain the rule genuinely does not apply to that specific case and you don't expect the violation to ever be fixed.
11+
12+
For **temporary** suppressions (e.g. the fix requires a large refactor that is out of scope), prefer widening the seatbelt baseline with `SEATBELT_INCREASE` instead — that keeps the violation visible so it can still be fixed later. See [`LINTING.md`](../../../../contributingGuides/LINTING.md) for full guidance on choosing between the two approaches.
13+
14+
When `eslint-disable` is used, a justification comment is required so that reviewers and future maintainers understand why the rule was deliberately suppressed.
1115

1216
### Incorrect
1317

@@ -37,6 +41,10 @@ Flag ONLY when ALL of these are true:
3741
- An ESLint rule is disabled (via `eslint-disable`, `eslint-disable-next-line`, etc.)
3842
- The disable statement lacks an accompanying comment explaining the reason
3943

44+
**Also consider flagging if:**
45+
46+
- `eslint-disable` is used for what appears to be a temporary/convenience suppression — in those cases, suggest `SEATBELT_INCREASE` as the preferred alternative (see [`LINTING.md`](../../../../contributingGuides/LINTING.md))
47+
4048
**DO NOT flag if:**
4149

4250
- The disablement is justified with a clear comment explaining why the rule is disabled

.storybook/main.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type {StorybookConfig} from 'storybook/internal/types';
1+
import type {StorybookConfig} from '@storybook/react-webpack5';
22

33
const main: StorybookConfig = {
44
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
@@ -15,6 +15,9 @@ const main: StorybookConfig = {
1515
options: {},
1616
},
1717
docs: {},
18+
typescript: {
19+
reactDocgen: false,
20+
},
1821
};
1922

2023
export default main;

.storybook/preview.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import {SafeAreaProvider} from 'react-native-safe-area-context';
55
import type {Parameters} from 'storybook/internal/types';
66
import EnvironmentProvider from '@components/EnvironmentContextProvider';
77
import OnyxListItemProvider from '@components/OnyxListItemProvider';
8+
import ScreenWrapperStatusContext from '@components/ScreenWrapper/ScreenWrapperStatusContext';
89
import {SearchContextProvider} from '@components/Search/SearchContext';
10+
import colors from '@styles/theme/colors';
911
import ComposeProviders from '@src/components/ComposeProviders';
1012
import HTMLEngineProvider from '@src/components/HTMLEngineProvider';
1113
import {LocaleContextProvider} from '@src/components/LocaleContextProvider';
@@ -35,7 +37,9 @@ const decorators = [
3537
SearchContextProvider,
3638
]}
3739
>
38-
<Story />
40+
<ScreenWrapperStatusContext.Provider value={{didScreenTransitionEnd: true, isSafeAreaTopPaddingApplied: false, isSafeAreaBottomPaddingApplied: false}}>
41+
<Story />
42+
</ScreenWrapperStatusContext.Provider>
3943
</ComposeProviders>
4044
),
4145
];
@@ -46,6 +50,16 @@ const parameters: Parameters = {
4650
color: /(background|color)$/i,
4751
},
4852
},
53+
backgrounds: {
54+
options: {
55+
dark: {name: 'Dark', value: colors.productDark100},
56+
light: {name: 'Light', value: colors.productLight100},
57+
},
58+
},
59+
};
60+
61+
const initialGlobals = {
62+
backgrounds: {value: 'dark'},
4963
};
5064

51-
export {decorators, parameters};
65+
export {decorators, parameters, initialGlobals};

Mobile-Expensify

__mocks__/@react-navigation/native/index.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type * as ReactNavigation from '@react-navigation/native';
2+
import {useEffect} from 'react';
23
import createAddListenerMock from '../../../tests/utils/createAddListenerMock';
34

45
const isJestEnv = process.env.NODE_ENV === 'test';
@@ -16,15 +17,14 @@ const {triggerTransitionEnd, addListener} = isJestEnv
1617
addListener: () => {},
1718
};
1819

19-
const useNavigation = isJestEnv
20-
? realReactNavigation.useNavigation
21-
: {
22-
navigate: isJestEnv ? jest.fn() : () => {},
23-
getState: () => ({
24-
routes: [],
25-
}),
26-
addListener,
27-
};
20+
const navigationMock = {
21+
navigate: () => {},
22+
getState: () => ({routes: []}),
23+
isFocused: () => true,
24+
addListener,
25+
};
26+
27+
const useNavigation = isJestEnv ? realReactNavigation.useNavigation : () => navigationMock;
2828

2929
type NativeNavigationMock = typeof ReactNavigation & {
3030
triggerTransitionEnd: () => void;
@@ -42,7 +42,12 @@ const useLinkProps = isJestEnv ? realReactNavigation.useLinkProps : () => null;
4242
const useLinkTo = isJestEnv ? realReactNavigation.useLinkTo : () => null;
4343
const useScrollToTop = isJestEnv ? realReactNavigation.useScrollToTop : () => null;
4444
const useRoute = isJestEnv ? realReactNavigation.useRoute : () => ({params: {}});
45-
const useFocusEffect = isJestEnv ? realReactNavigation.useFocusEffect : (callback: () => void) => callback();
45+
// Run callback in useEffect (like real useFocusEffect), not synchronously during render
46+
const useFocusEffect = isJestEnv
47+
? realReactNavigation.useFocusEffect
48+
: (callback: () => (() => void) | void) => {
49+
useEffect(() => callback(), [callback]);
50+
};
4651
const usePreventRemove = isJestEnv ? jest.fn() : () => {};
4752
const useNavigationState = isJestEnv ? realReactNavigation.useNavigationState : () => {};
4853

android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ android {
111111
minSdkVersion rootProject.ext.minSdkVersion
112112
targetSdkVersion rootProject.ext.targetSdkVersion
113113
multiDexEnabled rootProject.ext.multiDexEnabled
114-
versionCode 1009037506
115-
versionName "9.3.75-6"
114+
versionCode 1009037702
115+
versionName "9.3.77-2"
116116
// Supported language variants must be declared here to avoid from being removed during the compilation.
117117
// This also helps us to not include unnecessary language variants in the APK.
118118
resConfigs "en", "es"

config/eslint/eslint.seatbelt.tsv

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@
122122
"../../src/components/MoneyRequestAmountInput.tsx" "react-hooks/immutability" 2
123123
"../../src/components/MoneyRequestConfirmationList.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
124124
"../../src/components/MoneyRequestConfirmationList.tsx" "react-hooks/set-state-in-effect" 2
125-
"../../src/components/MoneyRequestHeaderSecondaryActions.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
126125
"../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 3
127126
"../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "react-hooks/refs" 6
128127
"../../src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx" "react-hooks/set-state-in-effect" 3
@@ -234,7 +233,6 @@
234233
"../../src/hooks/useSearchBulkActions.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 5
235234
"../../src/hooks/useSearchBulkActions.ts" "react-hooks/exhaustive-deps" 1
236235
"../../src/hooks/useSearchBulkActions.ts" "react-hooks/preserve-manual-memoization" 2
237-
"../../src/hooks/useSearchBulkActions.ts" "react-hooks/refs" 1
238236
"../../src/hooks/useSearchHighlightAndScroll.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
239237
"../../src/hooks/useSearchSelector/index.native.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
240238
"../../src/hooks/useSelectionModeReportActions.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 2
@@ -300,20 +298,17 @@
300298
"../../src/libs/PersonalDetailsUtils.ts" "rulesdir/no-onyx-connect" 2
301299
"../../src/libs/Pusher/index.native.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
302300
"../../src/libs/Pusher/index.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
303-
"../../src/libs/ReceiptUploadRetryHandler/handleFileRetry.ts" "no-restricted-syntax" 2
301+
"../../src/libs/ReceiptUploadRetryHandler/handleFileRetry.ts" "no-restricted-syntax" 1
304302
"../../src/libs/ReportActionItemEventHandler/index.android.ts" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
305-
"../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-deprecated/getReportName" 2
306-
"../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-deprecated/getReportNameCallback" 1
307303
"../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-deprecated/reportAction.sequenceNumber" 1
308304
"../../src/libs/ReportActionsUtils.ts" "@typescript-eslint/no-deprecated/reportAction?.originalMessage" 2
309305
"../../src/libs/ReportActionsUtils.ts" "rulesdir/no-onyx-connect" 3
310306
"../../src/libs/ReportNameUtils.ts" "@typescript-eslint/no-deprecated/translateLocal" 12
311307
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/getPolicy" 29
312-
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/getReportName" 10
308+
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/getReportName" 3
313309
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/getSearchReportName" 1
314310
"../../src/libs/ReportUtils.ts" "@typescript-eslint/no-deprecated/translateLocal" 43
315311
"../../src/libs/ReportUtils.ts" "rulesdir/no-onyx-connect" 17
316-
"../../src/libs/SearchUIUtils.ts" "@typescript-eslint/no-deprecated/getReportName" 1
317312
"../../src/libs/SearchUIUtils.ts" "@typescript-eslint/no-deprecated/getSearchReportName" 1
318313
"../../src/libs/SubscriptionUtils.ts" "rulesdir/no-onyx-connect" 1
319314
"../../src/libs/TransactionUtils/index.ts" "@typescript-eslint/no-deprecated/translateLocal" 5
@@ -504,7 +499,6 @@
504499
"../../src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
505500
"../../src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx" "react-hooks/preserve-manual-memoization" 1
506501
"../../src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx" "react-hooks/refs" 1
507-
"../../src/pages/inbox/report/ContextMenu/ContextMenuActions.tsx" "@typescript-eslint/no-deprecated/getReportNameDeprecated" 1
508502
"../../src/pages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsx" "@typescript-eslint/no-deprecated/ConfirmModal" 1
509503
"../../src/pages/inbox/report/ContextMenu/PopoverReportActionContextMenu.tsx" "react-hooks/refs" 29
510504
"../../src/pages/inbox/report/ListBoundaryLoader.tsx" "react-hooks/set-state-in-effect" 1
@@ -515,7 +509,6 @@
515509
"../../src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions.tsx" "react-hooks/refs" 7
516510
"../../src/pages/inbox/report/ReportActionCompose/SuggestionEmoji.tsx" "react-hooks/refs" 1
517511
"../../src/pages/inbox/report/ReportActionCompose/SuggestionMention.tsx" "react-hooks/refs" 3
518-
"../../src/pages/inbox/report/ReportActionItemMessage.tsx" "@typescript-eslint/no-deprecated/getReportName" 1
519512
"../../src/pages/inbox/report/ReportActionItemMessageEdit.tsx" "@typescript-eslint/no-deprecated/InteractionManager.runAfterInteractions" 1
520513
"../../src/pages/inbox/report/ReportActionItemMessageEdit.tsx" "no-restricted-syntax" 1
521514
"../../src/pages/inbox/report/ReportActionItemMessageEdit.tsx" "react-hooks/preserve-manual-memoization" 1
@@ -736,10 +729,8 @@
736729
"../../src/utils/keyboard/index.web.ts" "no-restricted-imports" 1
737730
"../../src/utils/keyboard/index.website.ts" "no-restricted-imports" 1
738731
"../../tests/actions/ReportTest.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 1
739-
"../../tests/perf-test/ReportUtils.perf-test.ts" "@typescript-eslint/no-deprecated/getReportName" 1
740732
"../../tests/unit/Navigation/TransitionTrackerTest.ts" "no-restricted-imports" 1
741733
"../../tests/unit/NextStepUtilsTest.ts" "@typescript-eslint/no-deprecated/buildNextStepNew" 23
742-
"../../tests/unit/ReportUtilsTest.ts" "@typescript-eslint/no-deprecated/getReportNameDeprecated" 6
743734
"../../tests/unit/canEditFieldOfMoneyRequestTest.ts" "@typescript-eslint/no-deprecated/randomReportAction.originalMessage" 3
744735
"../../tests/unit/submitDismissStrategiesTest.ts" "no-restricted-imports" 1
745736
"../../tests/unit/useSubStepTest.tsx" "@typescript-eslint/no-deprecated/useSubStep" 13

cspell.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"autosync",
7979
"avds",
8080
"AVURL",
81+
"bamboohr",
8182
"Bartek",
8283
"basehead",
8384
"baselined",
@@ -323,11 +324,13 @@
323324
"Heathrow",
324325
"helpdot",
325326
"helpsite",
327+
"hibob",
326328
"Highfive",
327329
"Highlightable",
328330
"HKBCCATT",
329331
"Hoverable",
330332
"hrefs",
333+
"hris",
331334
"HRMS",
332335
"HSBCSGS",
333336
"Humpty",
@@ -910,6 +913,7 @@
910913
"Monzo",
911914
"Novobanco",
912915
"Pekao",
916+
"Playroll",
913917
"Pleo",
914918
"Polska",
915919
"Postale",

docs/articles/expensify-classic/connections/Global-VaTax.md

Lines changed: 0 additions & 63 deletions
This file was deleted.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
title: Playroll
3+
description: Learn how to connect Expensify with Playroll to automatically sync approved expenses for HR and EOR processing.
4+
keywords: [Expensify Classic, Playroll, HR, EOR, expense sync, integration, payroll]
5+
internalScope: Audience is Workspace Admins and Workspace Owners. Covers connecting to Playroll for one-way expense syncing. Does not cover Playroll configuration beyond the Expensify integration.
6+
---
7+
8+
# Playroll
9+
10+
The Expensify-Playroll integration allows approved expenses in Expensify to automatically sync into Playroll for processing. This is a one-way integration (Expensify to Playroll only) — changes made after syncing will not sync back.
11+
12+
**Important:** Only approved expenses will sync, and expenses without receipts will not sync to Playroll.
13+
14+
---
15+
16+
## Who can connect Expensify to Playroll
17+
18+
You can connect Expensify to Playroll if:
19+
20+
- You are a Workspace Admin or Workspace Owner
21+
- You have permission to configure integrations in Playroll
22+
- You are signed into the Expensify account that owns or administers the workspace
23+
24+
This feature is not available on mobile.
25+
26+
---
27+
28+
## How to connect Expensify to Playroll
29+
30+
Before connecting Playroll, configure the required workspace settings in Expensify.
31+
32+
1. In the navigation tabs on the left, go to **Settings > Workspaces**.
33+
2. Select your workspace.
34+
3. Go to **Rules**.
35+
4. Enable **Public Receipt Visibility**.
36+
5. Set **Receipt Required Amount** to **$0**.
37+
6. Save your changes.
38+
39+
Then connect the integration in Playroll.
40+
41+
1. In Playroll, go to **Dashboard > Tools > Integrations**.
42+
2. Select **Expensify**.
43+
3. Open the **Configuration** tab.
44+
4. Click **Connect**.
45+
5. In Expensify, click **Find our Partner ID**.
46+
6. Copy the **Partner User ID** and **Partner User Secret**.
47+
7. Paste the credentials into Playroll.
48+
8. Click **Submit**.
49+
50+
After the integration connects successfully, Playroll displays the status as **Active / Connected**.
51+
52+
Employee email addresses in Expensify must match the Personal Email addresses in Playroll.
53+
54+
---
55+
56+
## What happens after you connect Expensify to Playroll
57+
58+
After the integration is connected:
59+
60+
- Approved expenses automatically sync from Expensify to Playroll
61+
- Expenses without attached receipts do not sync
62+
- Changes made after syncing do not sync back into Expensify
63+
- Expense category mappings in Playroll determine how synced expenses appear in Playroll
64+
65+
---
66+
67+
# FAQ
68+
69+
## Why are expenses not syncing to Playroll?
70+
71+
Expenses may not sync if:
72+
73+
- The expense is not approved
74+
- The expense does not include a receipt
75+
- **Public Receipt Visibility** is disabled
76+
- Employee email addresses do not match between Expensify and Playroll
77+
- Expense categories are not mapped in Playroll
78+
79+
## Can members connect Expensify to Playroll?
80+
81+
No. Only Workspace Admins and Workspace Owners can configure the integration.
82+
83+
## Does the Playroll integration sync changes back into Expensify?
84+
85+
No. The integration only syncs expenses from Expensify to Playroll.
86+
87+
## Can I connect Expensify to Playroll on mobile?
88+
89+
No. This feature is not available on mobile.

0 commit comments

Comments
 (0)