@salesforce/webapp-template-feature-react-global-search-experimental 1.71.0 → 1.71.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.md +16 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/README.md +75 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/package.json +3 -3
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/alerts/status-alert.tsx +36 -32
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/layouts/card-layout.tsx +29 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{api → features/global-search/api}/objectDetailService.ts +2 -2
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/DetailHeader.tsx +1 -1
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/filters/FilterInput.tsx +2 -2
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/filters/FilterSelect.tsx +2 -2
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components/filters}/FiltersPanel.tsx +15 -10
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/forms/filters-form.tsx +4 -4
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/forms/submit-button.tsx +3 -3
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components/shared → features/global-search/components/search}/GlobalSearchInput.tsx +4 -4
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/search/SearchPagination.tsx +3 -3
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components/search}/SearchResultCard.tsx +10 -5
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/search/SearchResultsPanel.tsx +5 -5
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components/shared}/LoadingFallback.tsx +1 -1
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/features/global-search/filters/FilterInput.tsx +55 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/features/global-search/filters/FilterSelect.tsx +72 -0
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{hooks → features/global-search/hooks}/form.tsx +9 -4
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{pages → features/global-search/pages}/DetailPage.tsx +4 -4
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{pages → features/global-search/pages}/GlobalSearch.tsx +4 -4
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/pages/Home.tsx +1 -1
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/routes.tsx +3 -3
- package/dist/package.json +1 -1
- package/package.json +2 -1
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/components/layout/card-layout.tsx +0 -19
- package/dist/force-app/main/default/webapplications/feature-react-global-search/src/features/global-search/index.ts +0 -33
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{api → features/global-search/api}/index.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{api → features/global-search/api}/objectInfoGraphQLService.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{api → features/global-search/api}/objectInfoService.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{api → features/global-search/api}/recordListGraphQLService.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/DetailFields.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/DetailForm.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/DetailLayoutSections.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/Section.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/SectionRow.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/UiApiDetailForm.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/FieldValueDisplay.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/FormattedAddress.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/FormattedEmail.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/FormattedPhone.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/FormattedText.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/FormattedUrl.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/detail/formatted/index.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/filters/FilterField.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/search/ResultCardFields.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{components → features/global-search/components}/search/SearchHeader.tsx +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{hooks → features/global-search/hooks}/index.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{hooks → features/global-search/hooks}/useObjectInfoBatch.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{hooks → features/global-search/hooks}/useObjectSearchData.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{hooks → features/global-search/hooks}/useRecordDetailLayout.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{hooks → features/global-search/hooks}/useRecordListGraphQL.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{types → features/global-search/types}/filters/filters.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{types → features/global-search/types}/filters/picklist.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{types → features/global-search/types}/index.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{types → features/global-search/types}/objectInfo/objectInfo.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{types → features/global-search/types}/recordDetail/recordDetail.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{types → features/global-search/types}/search/searchResults.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/apiUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/cacheUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/debounce.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/fieldUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/fieldValueExtractor.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/filterUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/formDataTransformUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/formUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/graphQLNodeFieldUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/graphQLObjectInfoAdapter.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/graphQLRecordAdapter.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/index.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/layoutTransformUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/linkUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/paginationUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/recordUtils.ts +0 -0
- /package/dist/force-app/main/default/webapplications/feature-react-global-search/src/{utils → features/global-search/utils}/sanitizationUtils.ts +0 -0
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.71.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.71.1...v1.71.2) (2026-03-05)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [1.71.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.71.0...v1.71.1) (2026-03-05)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# [1.71.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.70.0...v1.71.0) (2026-03-05)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Base React App
|
|
2
|
+
|
|
3
|
+
Base React App is a template application that demonstrates how to build a React web app on the Salesforce platform with Vite, TypeScript, Tailwind, shadcn/ui, and the Salesforce Web App SDK. It provides a minimal shell (home, 404), routing, and GraphQL codegen support so feature apps can extend it via the patches pipeline.
|
|
4
|
+
|
|
5
|
+
This web application lives inside an SFDX project. The project root is the directory that contains `force-app/` and `sfdx-project.json`. Run the commands in the sections below from the paths indicated.
|
|
6
|
+
|
|
7
|
+
## Table of contents
|
|
8
|
+
|
|
9
|
+
- [Run (development)](#run-development)
|
|
10
|
+
- [Build](#build)
|
|
11
|
+
- [Deploy](#deploy)
|
|
12
|
+
- [Test](#test)
|
|
13
|
+
|
|
14
|
+
## Run (development)
|
|
15
|
+
|
|
16
|
+
From the web app directory (`force-app/main/default/webapplications/base-react-app`):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
npm run dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This starts the Vite dev server (e.g. http://localhost:5173). Use `npm run dev:design` to run in design mode.
|
|
24
|
+
|
|
25
|
+
## Build
|
|
26
|
+
|
|
27
|
+
From the web app directory:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install
|
|
31
|
+
npm run build
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The production build is written to `dist/` inside the web app folder. Deploy using the steps in [Deploy](#deploy).
|
|
35
|
+
|
|
36
|
+
## Deploy
|
|
37
|
+
|
|
38
|
+
From the **SFDX project root** (the directory that contains `force-app/`):
|
|
39
|
+
|
|
40
|
+
1. Build the web app:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd force-app/main/default/webapplications/base-react-app && npm install && npm run build && cd -
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. Deploy the web app only:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
sf project deploy start --source-dir force-app/main/default/webapplications --target-org <alias>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or deploy all metadata:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sf project deploy start --source-dir force-app --target-org <alias>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Replace `<alias>` with your target org alias.
|
|
59
|
+
|
|
60
|
+
## Test
|
|
61
|
+
|
|
62
|
+
From the web app directory:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install
|
|
66
|
+
npm run test
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This runs the unit test suite (Vitest). For end-to-end tests from the **base-react-app package root**:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm run test:e2e
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This installs dependencies, builds with E2E asset rewrites, and runs Playwright. Ensure Chromium is installed (`npx playwright install chromium` if needed).
|
package/dist/force-app/main/default/webapplications/feature-react-global-search/package.json
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@salesforce/sdk-data": "^1.71.
|
|
19
|
-
"@salesforce/webapp-experimental": "^1.71.
|
|
18
|
+
"@salesforce/sdk-data": "^1.71.2",
|
|
19
|
+
"@salesforce/webapp-experimental": "^1.71.2",
|
|
20
20
|
"@tailwindcss/vite": "^4.1.17",
|
|
21
21
|
"@tanstack/react-form": "^1.28.4",
|
|
22
22
|
"class-variance-authority": "^0.7.1",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
41
41
|
"@graphql-tools/utils": "^11.0.0",
|
|
42
42
|
"@playwright/test": "^1.49.0",
|
|
43
|
-
"@salesforce/vite-plugin-webapp-experimental": "^1.71.
|
|
43
|
+
"@salesforce/vite-plugin-webapp-experimental": "^1.71.2",
|
|
44
44
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
45
|
"@testing-library/react": "^16.1.0",
|
|
46
46
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -1,45 +1,49 @@
|
|
|
1
|
-
import { cva, type VariantProps } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
2
|
+
import { AlertCircleIcon, CheckCircle2Icon } from 'lucide-react';
|
|
3
|
+
import { Alert, AlertDescription } from '../../components/ui/alert';
|
|
4
|
+
import { useId } from 'react';
|
|
5
5
|
|
|
6
|
-
const statusAlertVariants = cva(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
const statusAlertVariants = cva('', {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
error: '',
|
|
10
|
+
success: '',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
defaultVariants: {
|
|
14
|
+
variant: 'error',
|
|
15
|
+
},
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
interface StatusAlertProps extends VariantProps<typeof statusAlertVariants> {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
children?: React.ReactNode;
|
|
20
|
+
/** Alert variant type. @default "error" */
|
|
21
|
+
variant?: 'error' | 'success';
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Status alert component for displaying error or success messages.
|
|
26
26
|
* Returns null if no children are provided.
|
|
27
27
|
*/
|
|
28
|
-
export function StatusAlert({ children, variant =
|
|
29
|
-
|
|
28
|
+
export function StatusAlert({ children, variant = 'error' }: StatusAlertProps) {
|
|
29
|
+
if (!children) return null;
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const isError = variant === 'error';
|
|
32
|
+
const descriptionId = useId();
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
return (
|
|
35
|
+
<Alert
|
|
36
|
+
variant={isError ? 'destructive' : 'default'}
|
|
37
|
+
className={statusAlertVariants({ variant })}
|
|
38
|
+
aria-describedby={descriptionId}
|
|
39
|
+
role={isError ? 'alert' : 'status'}
|
|
40
|
+
>
|
|
41
|
+
{isError ? (
|
|
42
|
+
<AlertCircleIcon aria-hidden="true" />
|
|
43
|
+
) : (
|
|
44
|
+
<CheckCircle2Icon aria-hidden="true" />
|
|
45
|
+
)}
|
|
46
|
+
<AlertDescription id={descriptionId}>{children}</AlertDescription>
|
|
47
|
+
</Alert>
|
|
48
|
+
);
|
|
45
49
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardContent,
|
|
4
|
+
CardDescription,
|
|
5
|
+
CardHeader,
|
|
6
|
+
CardTitle,
|
|
7
|
+
} from '../../components/ui/card';
|
|
8
|
+
|
|
9
|
+
interface CardLayoutProps {
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Card layout component for authentication pages.
|
|
17
|
+
* Provides CardHeader with title and optional description, and CardContent.
|
|
18
|
+
*/
|
|
19
|
+
export function CardLayout({ title, description, children }: CardLayoutProps) {
|
|
20
|
+
return (
|
|
21
|
+
<Card>
|
|
22
|
+
<CardHeader>
|
|
23
|
+
<CardTitle className="text-2xl">{title}</CardTitle>
|
|
24
|
+
{description && <CardDescription>{description}</CardDescription>}
|
|
25
|
+
</CardHeader>
|
|
26
|
+
<CardContent>{children}</CardContent>
|
|
27
|
+
</Card>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -11,9 +11,9 @@ import { uiApiClient } from "@salesforce/webapp-experimental/api";
|
|
|
11
11
|
import type { LayoutResponse } from "../types/recordDetail/recordDetail";
|
|
12
12
|
import { LayoutResponseSchema } from "../types/recordDetail/recordDetail";
|
|
13
13
|
import { fetchAndValidate, safeEncodePath } from "../utils/apiUtils";
|
|
14
|
-
import { objectInfoService } from "
|
|
14
|
+
import { objectInfoService } from "./objectInfoService";
|
|
15
15
|
import type { ObjectInfoResult } from "../types/objectInfo/objectInfo";
|
|
16
|
-
import { getRecordByIdGraphQL, type GraphQLRecordNode } from "
|
|
16
|
+
import { getRecordByIdGraphQL, type GraphQLRecordNode } from "./recordListGraphQLService";
|
|
17
17
|
import type { Column } from "../types/search/searchResults";
|
|
18
18
|
import { calculateFieldsToFetch } from "../utils/recordUtils";
|
|
19
19
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @param title - Record title (e.g. record name) shown next to the back control.
|
|
5
5
|
* @param onBack - Called when the user activates the back control.
|
|
6
6
|
*/
|
|
7
|
-
import { Button } from "
|
|
7
|
+
import { Button } from "../../../../components/ui/button";
|
|
8
8
|
import { ArrowLeft } from "lucide-react";
|
|
9
9
|
|
|
10
10
|
interface DetailHeaderProps {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* />
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
|
-
import { Input } from "
|
|
26
|
-
import { Field, FieldLabel, FieldDescription } from "
|
|
25
|
+
import { Input } from "../../../../components/ui/input";
|
|
26
|
+
import { Field, FieldLabel, FieldDescription } from "../../../../components/ui/field";
|
|
27
27
|
import type { Filter } from "../../types/filters/filters";
|
|
28
28
|
|
|
29
29
|
interface FilterInputProps {
|
|
@@ -30,8 +30,8 @@ import {
|
|
|
30
30
|
SelectItem,
|
|
31
31
|
SelectTrigger,
|
|
32
32
|
SelectValue,
|
|
33
|
-
} from "
|
|
34
|
-
import { Field, FieldLabel, FieldDescription } from "
|
|
33
|
+
} from "../../../../components/ui/select";
|
|
34
|
+
import { Field, FieldLabel, FieldDescription } from "../../../../components/ui/field";
|
|
35
35
|
import type { Filter } from "../../types/filters/filters";
|
|
36
36
|
import type { PicklistValue } from "../../types/filters/picklist";
|
|
37
37
|
|
|
@@ -27,16 +27,21 @@
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
import { useState, useMemo, useCallback, useEffect, useRef } from "react";
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
import
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
Card,
|
|
32
|
+
CardContent,
|
|
33
|
+
CardHeader,
|
|
34
|
+
CardTitle,
|
|
35
|
+
} from "../../../../components/ui/card";
|
|
36
|
+
import { Skeleton } from "../../../../components/ui/skeleton";
|
|
37
|
+
import { FiltersForm } from "../forms/filters-form";
|
|
38
|
+
import { Field, FieldLabel, FieldDescription } from "../../../../components/ui/field";
|
|
39
|
+
import { useAppForm, validateRangeValues } from "../../hooks/form";
|
|
40
|
+
import type { Filter, FilterCriteria } from "../../types/filters/filters";
|
|
41
|
+
import type { PicklistValue } from "../../types/filters/picklist";
|
|
42
|
+
import { parseFilterValue } from "../../utils/filterUtils";
|
|
43
|
+
import { sanitizeFilterValue } from "../../utils/sanitizationUtils";
|
|
44
|
+
import { getFormValueByPath } from "../../utils/formUtils";
|
|
40
45
|
|
|
41
46
|
interface FiltersPanelProps {
|
|
42
47
|
filters: Filter[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { FieldGroup } from "
|
|
2
|
-
import { StatusAlert } from "
|
|
3
|
-
import { CardLayout } from "
|
|
1
|
+
import { FieldGroup } from "../../../../components/ui/field";
|
|
2
|
+
import { StatusAlert } from "../../../../components/alerts/status-alert";
|
|
3
|
+
import { CardLayout } from "../../../../components/layouts/card-layout";
|
|
4
4
|
import { SubmitButton } from "./submit-button";
|
|
5
|
-
import { Button } from "
|
|
5
|
+
import { Button } from "../../../../components/ui/button";
|
|
6
6
|
import { useFormContext } from "../../hooks/form";
|
|
7
7
|
import { useId, useEffect, useRef } from "react";
|
|
8
8
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Button } from "
|
|
2
|
-
import { Spinner } from "
|
|
3
|
-
import { cn } from "
|
|
1
|
+
import { Button } from "../../../../components/ui/button";
|
|
2
|
+
import { Spinner } from "../../../../components/ui/spinner";
|
|
3
|
+
import { cn } from "../../../../lib/utils";
|
|
4
4
|
import { useFormContext } from "../../hooks/form";
|
|
5
5
|
|
|
6
6
|
interface SubmitButtonProps extends Omit<React.ComponentProps<typeof Button>, "type" | "disabled"> {
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
import { useState, useCallback, useMemo, useId } from "react";
|
|
8
8
|
import type { KeyboardEvent, ChangeEvent } from "react";
|
|
9
9
|
import { useNavigate } from "react-router";
|
|
10
|
-
import { Card, CardContent } from "
|
|
11
|
-
import { Input } from "
|
|
12
|
-
import { Button } from "
|
|
10
|
+
import { Card, CardContent } from "../../../../components/ui/card";
|
|
11
|
+
import { Input } from "../../../../components/ui/input";
|
|
12
|
+
import { Button } from "../../../../components/ui/button";
|
|
13
13
|
import { Search } from "lucide-react";
|
|
14
|
-
import { OBJECT_API_NAMES } from "
|
|
14
|
+
import { OBJECT_API_NAMES } from "../../../../constants";
|
|
15
15
|
import { useObjectInfoBatch } from "../../hooks/useObjectInfoBatch";
|
|
16
16
|
|
|
17
17
|
const BROWSE_SEGMENT = "browse__all";
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
* - Previous disabled when !hasPreviousPage (cursor stack enables prev when pageIndex > 0).
|
|
10
10
|
* - Next disabled when !hasNextPage or nextPageToken is null.
|
|
11
11
|
*/
|
|
12
|
-
import { Button } from "
|
|
12
|
+
import { Button } from "../../../../components/ui/button";
|
|
13
13
|
import {
|
|
14
14
|
Select,
|
|
15
15
|
SelectContent,
|
|
16
16
|
SelectItem,
|
|
17
17
|
SelectTrigger,
|
|
18
18
|
SelectValue,
|
|
19
|
-
} from "
|
|
20
|
-
import { Label } from "
|
|
19
|
+
} from "../../../../components/ui/select";
|
|
20
|
+
import { Label } from "../../../../components/ui/label";
|
|
21
21
|
import { PAGE_SIZE_OPTIONS, getValidPageSize, isValidPageSize } from "../../utils/paginationUtils";
|
|
22
22
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
23
23
|
|
|
@@ -25,11 +25,16 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { useNavigate } from "react-router";
|
|
27
27
|
import { useMemo, useCallback } from "react";
|
|
28
|
-
import {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
import {
|
|
29
|
+
Card,
|
|
30
|
+
CardContent,
|
|
31
|
+
CardHeader,
|
|
32
|
+
CardTitle,
|
|
33
|
+
} from "../../../../components/ui/card";
|
|
34
|
+
import type { Column, SearchResultRecordData } from "../../types/search/searchResults";
|
|
35
|
+
import { getNestedFieldValue } from "../../utils/fieldUtils";
|
|
36
|
+
import ResultCardFields from "./ResultCardFields";
|
|
37
|
+
import { OBJECT_API_NAMES } from "../../../../constants";
|
|
33
38
|
|
|
34
39
|
interface SearchResultCardProps {
|
|
35
40
|
record: SearchResultRecordData;
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
* ```
|
|
33
33
|
*/
|
|
34
34
|
import { useMemo } from "react";
|
|
35
|
-
import { Alert, AlertDescription, AlertTitle } from "
|
|
36
|
-
import { Skeleton } from "
|
|
35
|
+
import { Alert, AlertDescription, AlertTitle } from "../../../../components/ui/alert";
|
|
36
|
+
import { Skeleton } from "../../../../components/ui/skeleton";
|
|
37
37
|
import { AlertCircle } from "lucide-react";
|
|
38
38
|
import {
|
|
39
39
|
Select,
|
|
@@ -41,9 +41,9 @@ import {
|
|
|
41
41
|
SelectItem,
|
|
42
42
|
SelectTrigger,
|
|
43
43
|
SelectValue,
|
|
44
|
-
} from "
|
|
45
|
-
import { Label } from "
|
|
46
|
-
import SearchResultCard from "
|
|
44
|
+
} from "../../../../components/ui/select";
|
|
45
|
+
import { Label } from "../../../../components/ui/label";
|
|
46
|
+
import SearchResultCard from "./SearchResultCard";
|
|
47
47
|
import SearchPagination from "./SearchPagination";
|
|
48
48
|
import type { Column, SearchResultRecord } from "../../types/search/searchResults";
|
|
49
49
|
import { getSafeKey } from "../../utils/recordUtils";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilterInput Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a text input field for filter values.
|
|
5
|
+
* Used for filters that don't have a picklist (affordance !== 'select').
|
|
6
|
+
*
|
|
7
|
+
* @param filter - Filter definition containing field path, label, and attributes
|
|
8
|
+
* @param value - Current filter input value
|
|
9
|
+
* @param onChange - Callback when input value changes
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* - Displays filter label or field path as the label
|
|
13
|
+
* - Shows placeholder text from filter attributes or generates default
|
|
14
|
+
* - Displays help message if available
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <FilterInput
|
|
19
|
+
* filter={textFilter}
|
|
20
|
+
* value={filterValue}
|
|
21
|
+
* onChange={(value) => setFilterValue(value)}
|
|
22
|
+
* />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import { Input } from "../../../components/ui/input";
|
|
26
|
+
import { Field, FieldLabel, FieldDescription } from "../../../components/ui/field";
|
|
27
|
+
import type { Filter } from "../types/filters/filters";
|
|
28
|
+
|
|
29
|
+
interface FilterInputProps {
|
|
30
|
+
filter: Filter;
|
|
31
|
+
value: string;
|
|
32
|
+
onChange: (value: string) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function FilterInput({ filter, value, onChange }: FilterInputProps) {
|
|
36
|
+
return (
|
|
37
|
+
<Field>
|
|
38
|
+
<FieldLabel htmlFor={filter.targetFieldPath}>
|
|
39
|
+
{filter.label || filter.targetFieldPath}
|
|
40
|
+
</FieldLabel>
|
|
41
|
+
<Input
|
|
42
|
+
id={filter.targetFieldPath}
|
|
43
|
+
type="text"
|
|
44
|
+
value={value}
|
|
45
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
|
|
46
|
+
placeholder={
|
|
47
|
+
filter.attributes?.placeholder ||
|
|
48
|
+
`Enter ${(filter.label || filter.targetFieldPath).toLowerCase()}`
|
|
49
|
+
}
|
|
50
|
+
aria-label={filter.label || filter.targetFieldPath}
|
|
51
|
+
/>
|
|
52
|
+
{filter.helpMessage && <FieldDescription>{filter.helpMessage}</FieldDescription>}
|
|
53
|
+
</Field>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilterSelect Component
|
|
3
|
+
*
|
|
4
|
+
* Renders a dropdown select field for filter values with picklist options.
|
|
5
|
+
* Used for filters with affordance === 'select'.
|
|
6
|
+
*
|
|
7
|
+
* @param filter - Filter definition containing field path, label, and attributes
|
|
8
|
+
* @param value - Currently selected filter value
|
|
9
|
+
* @param options - Array of picklist values to display as options
|
|
10
|
+
* @param onChange - Callback when selection changes
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* - Filters out invalid options (null/undefined values)
|
|
14
|
+
* - Displays option label if available, otherwise uses value
|
|
15
|
+
* - Shows placeholder from filter attributes or default "Select..."
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <FilterSelect
|
|
20
|
+
* filter={selectFilter}
|
|
21
|
+
* value={selectedValue}
|
|
22
|
+
* options={picklistOptions}
|
|
23
|
+
* onChange={(value) => setSelectedValue(value)}
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import {
|
|
28
|
+
Select,
|
|
29
|
+
SelectContent,
|
|
30
|
+
SelectItem,
|
|
31
|
+
SelectTrigger,
|
|
32
|
+
SelectValue,
|
|
33
|
+
} from "../../../components/ui/select";
|
|
34
|
+
import { Field, FieldLabel, FieldDescription } from "../../../components/ui/field";
|
|
35
|
+
import type { Filter } from "../types/filters/filters";
|
|
36
|
+
import type { PicklistValue } from "../types/filters/picklist";
|
|
37
|
+
|
|
38
|
+
interface FilterSelectProps {
|
|
39
|
+
filter: Filter;
|
|
40
|
+
value: string;
|
|
41
|
+
options: PicklistValue[];
|
|
42
|
+
onChange: (value: string) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default function FilterSelect({ filter, value, options, onChange }: FilterSelectProps) {
|
|
46
|
+
return (
|
|
47
|
+
<Field>
|
|
48
|
+
<FieldLabel htmlFor={filter.targetFieldPath}>
|
|
49
|
+
{filter.label || filter.targetFieldPath}
|
|
50
|
+
</FieldLabel>
|
|
51
|
+
<Select value={value} onValueChange={onChange}>
|
|
52
|
+
<SelectTrigger
|
|
53
|
+
id={filter.targetFieldPath}
|
|
54
|
+
aria-label={filter.label || filter.targetFieldPath}
|
|
55
|
+
>
|
|
56
|
+
<SelectValue placeholder={filter.attributes?.placeholder || "Select..."} />
|
|
57
|
+
</SelectTrigger>
|
|
58
|
+
<SelectContent>
|
|
59
|
+
{options.map((option) => {
|
|
60
|
+
if (!option || !option.value) return null;
|
|
61
|
+
return (
|
|
62
|
+
<SelectItem key={option.value} value={option.value}>
|
|
63
|
+
{option.label || option.value}
|
|
64
|
+
</SelectItem>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</SelectContent>
|
|
68
|
+
</Select>
|
|
69
|
+
{filter.helpMessage && <FieldDescription>{filter.helpMessage}</FieldDescription>}
|
|
70
|
+
</Field>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { useId } from "react";
|
|
2
2
|
import { createFormHookContexts, createFormHook } from "@tanstack/react-form";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
Field,
|
|
5
|
+
FieldDescription,
|
|
6
|
+
FieldError,
|
|
7
|
+
FieldLabel,
|
|
8
|
+
} from "../../../components/ui/field";
|
|
9
|
+
import { Input } from "../../../components/ui/input";
|
|
5
10
|
import {
|
|
6
11
|
Select,
|
|
7
12
|
SelectContent,
|
|
8
13
|
SelectItem,
|
|
9
14
|
SelectTrigger,
|
|
10
15
|
SelectValue,
|
|
11
|
-
} from "
|
|
12
|
-
import { cn } from "
|
|
16
|
+
} from "../../../components/ui/select";
|
|
17
|
+
import { cn } from "../../../lib/utils";
|
|
13
18
|
import type { PicklistValue } from "../types/filters/picklist";
|
|
14
19
|
import { getUniqueErrors } from "../utils/formUtils";
|
|
15
20
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
2
|
import { useParams, useNavigate } from "react-router";
|
|
3
|
-
import { Card, CardContent, CardHeader, CardTitle } from "
|
|
4
|
-
import { Skeleton } from "
|
|
5
|
-
import { Alert, AlertDescription, AlertTitle } from "
|
|
3
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card";
|
|
4
|
+
import { Skeleton } from "../../../components/ui/skeleton";
|
|
5
|
+
import { Alert, AlertDescription, AlertTitle } from "../../../components/ui/alert";
|
|
6
6
|
import { AlertCircle } from "lucide-react";
|
|
7
7
|
import DetailHeader from "../components/detail/DetailHeader";
|
|
8
8
|
import { UiApiDetailForm } from "../components/detail/UiApiDetailForm";
|
|
9
|
-
import { OBJECT_API_NAMES, DEFAULT_DETAIL_PAGE_TITLE } from "
|
|
9
|
+
import { OBJECT_API_NAMES, DEFAULT_DETAIL_PAGE_TITLE } from "../../../constants";
|
|
10
10
|
import { toRecordDisplayNameMetadata } from "../utils/fieldUtils";
|
|
11
11
|
import { useRecordDetailLayout } from "../hooks/useRecordDetailLayout";
|
|
12
12
|
import { getGraphQLRecordDisplayName } from "../utils/graphQLNodeFieldUtils";
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { useMemo, useState, useCallback, useEffect, useRef } from "react";
|
|
15
15
|
import { useParams } from "react-router";
|
|
16
|
-
import { OBJECT_API_NAMES, DEFAULT_PAGE_SIZE } from "
|
|
16
|
+
import { OBJECT_API_NAMES, DEFAULT_PAGE_SIZE } from "../../../constants";
|
|
17
17
|
import { useObjectListMetadata } from "../hooks/useObjectSearchData";
|
|
18
18
|
import { useObjectInfoBatch } from "../hooks/useObjectInfoBatch";
|
|
19
19
|
import { useRecordListGraphQL } from "../hooks/useRecordListGraphQL";
|
|
20
|
-
import FiltersPanel from "../components/FiltersPanel";
|
|
20
|
+
import FiltersPanel from "../components/filters/FiltersPanel";
|
|
21
21
|
import SearchHeader from "../components/search/SearchHeader";
|
|
22
22
|
import SearchResultsPanel from "../components/search/SearchResultsPanel";
|
|
23
|
-
import { Card, CardContent, CardHeader, CardTitle } from "
|
|
24
|
-
import { Skeleton } from "
|
|
23
|
+
import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card";
|
|
24
|
+
import { Skeleton } from "../../../components/ui/skeleton";
|
|
25
25
|
import type { FilterCriteria } from "../types/filters/filters";
|
|
26
26
|
import type { SearchResultRecord } from "../types/search/searchResults";
|
|
27
27
|
import { graphQLNodeToSearchResultRecordData } from "../utils/graphQLRecordAdapter";
|
package/dist/force-app/main/default/webapplications/feature-react-global-search/src/routes.tsx
CHANGED
|
@@ -2,10 +2,10 @@ import type { RouteObject } from 'react-router';
|
|
|
2
2
|
import AppLayout from './appLayout';
|
|
3
3
|
import Home from './pages/Home';
|
|
4
4
|
import NotFound from './pages/NotFound';
|
|
5
|
-
import GlobalSearch from "./pages/GlobalSearch";
|
|
6
|
-
import DetailPage from "./pages/DetailPage";
|
|
5
|
+
import GlobalSearch from "./features/global-search/pages/GlobalSearch";
|
|
6
|
+
import DetailPage from "./features/global-search/pages/DetailPage";
|
|
7
7
|
import { Suspense } from "react";
|
|
8
|
-
import LoadingFallback from "./components/LoadingFallback";
|
|
8
|
+
import LoadingFallback from "./features/global-search/components/shared/LoadingFallback";
|
|
9
9
|
|
|
10
10
|
export const routes: RouteObject[] = [
|
|
11
11
|
{
|
package/dist/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/webapp-template-feature-react-global-search-experimental",
|
|
3
|
-
"version": "1.71.
|
|
3
|
+
"version": "1.71.2",
|
|
4
4
|
"description": "Global search feature for Salesforce objects with filtering and pagination",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"author": "",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"@types/react": "^19.2.7",
|
|
20
20
|
"@types/react-dom": "^19.2.3",
|
|
21
21
|
"@types/react-router-dom": "^5.3.3",
|
|
22
|
+
"class-variance-authority": "^0.7.1",
|
|
22
23
|
"lucide-react": "^0.468.0",
|
|
23
24
|
"react-dom": "^19.2.1",
|
|
24
25
|
"react-router": "^7.10.1",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
|
|
2
|
-
|
|
3
|
-
interface CardLayoutProps {
|
|
4
|
-
title: string;
|
|
5
|
-
description?: string;
|
|
6
|
-
children: React.ReactNode;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function CardLayout({ title, description, children }: CardLayoutProps) {
|
|
10
|
-
return (
|
|
11
|
-
<Card className="w-full">
|
|
12
|
-
<CardHeader>
|
|
13
|
-
<CardTitle className="text-2xl">{title}</CardTitle>
|
|
14
|
-
{description && <CardDescription>{description}</CardDescription>}
|
|
15
|
-
</CardHeader>
|
|
16
|
-
<CardContent>{children}</CardContent>
|
|
17
|
-
</Card>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Global Search Feature - Public API
|
|
3
|
-
*
|
|
4
|
-
* Main entry point for the global search feature.
|
|
5
|
-
* Exports all public APIs, components, and utilities that can be consumed by other packages.
|
|
6
|
-
*
|
|
7
|
-
* @remarks
|
|
8
|
-
* This is the public API of the feature. Components and utilities exported here
|
|
9
|
-
* can be imported by other packages that depend on this feature.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```tsx
|
|
13
|
-
* // In another package's appLayout.tsx
|
|
14
|
-
* import { GlobalSearchInput } from 'feature-react-global-search';
|
|
15
|
-
*
|
|
16
|
-
* export default function AppLayout() {
|
|
17
|
-
* return (
|
|
18
|
-
* <div>
|
|
19
|
-
* <GlobalSearchInput />
|
|
20
|
-
* <Outlet />
|
|
21
|
-
* </div>
|
|
22
|
-
* );
|
|
23
|
-
* }
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
export * from "../../api";
|
|
28
|
-
export * from "../../hooks";
|
|
29
|
-
export * from "../../utils";
|
|
30
|
-
export * from "../../constants";
|
|
31
|
-
export * from "../../routes";
|
|
32
|
-
export * from "../../hooks";
|
|
33
|
-
export * from "../../types";
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|