@salesforce/ui-bundle-template-app-react-template-b2e 11.64.0 → 11.66.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 +56 -0
- package/dist/force-app/main/default/uiBundles/reactinternalapp/package.json +4 -4
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/cmsQueryFragment.ts +14 -3
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/index.ts +1 -0
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/searchChannel.ts +17 -1
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/types.ts +2 -0
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/MergedSearchResults.tsx +16 -4
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/SearchResults.tsx +1 -0
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/SourceSection.tsx +21 -2
- package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/hooks/useSearch.ts +8 -4
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,62 @@
|
|
|
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
|
+
## [11.66.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.66.1...v11.66.2) (2026-08-20)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [11.66.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.66.0...v11.66.1) (2026-08-20)
|
|
15
|
+
|
|
16
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## [11.66.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.65.3...v11.66.0) (2026-08-19)
|
|
23
|
+
|
|
24
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [11.65.3](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.65.2...v11.65.3) (2026-08-19)
|
|
31
|
+
|
|
32
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## [11.65.2](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.65.1...v11.65.2) (2026-08-19)
|
|
39
|
+
|
|
40
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## [11.65.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.65.0...v11.65.1) (2026-08-19)
|
|
47
|
+
|
|
48
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## [11.65.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.64.0...v11.65.0) (2026-08-19)
|
|
55
|
+
|
|
56
|
+
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
6
62
|
## [11.64.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v11.63.0...v11.64.0) (2026-08-18)
|
|
7
63
|
|
|
8
64
|
**Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@salesforce/platform-sdk": "^11.
|
|
22
|
-
"@salesforce/ui-bundle": "^11.
|
|
21
|
+
"@salesforce/platform-sdk": "^11.66.2",
|
|
22
|
+
"@salesforce/ui-bundle": "^11.66.2",
|
|
23
23
|
"@tailwindcss/vite": "^4.1.17",
|
|
24
24
|
"class-variance-authority": "^0.7.1",
|
|
25
25
|
"clsx": "^2.1.1",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
46
46
|
"@graphql-tools/utils": "^11.0.0",
|
|
47
47
|
"@playwright/test": "^1.49.0",
|
|
48
|
-
"@salesforce/graphiti": "^11.
|
|
49
|
-
"@salesforce/vite-plugin-ui-bundle": "^11.
|
|
48
|
+
"@salesforce/graphiti": "^11.66.2",
|
|
49
|
+
"@salesforce/vite-plugin-ui-bundle": "^11.66.2",
|
|
50
50
|
"@testing-library/jest-dom": "^6.6.3",
|
|
51
51
|
"@testing-library/react": "^16.1.0",
|
|
52
52
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* The `$cmsContentTypeFQNs` declaration, its `contentTypeFQNs:` argument, and
|
|
7
7
|
* its variable value are all emitted only when a non-empty content-type list is
|
|
8
|
-
* passed; on the bootstrap call (no types known) they are omitted entirely.
|
|
8
|
+
* passed; on the bootstrap call (no types known) they are omitted entirely. The
|
|
9
|
+
* `$cmsLanguage` pieces follow the same conditional pattern.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import type { QueryFragmentContribution } from "../types";
|
|
@@ -26,6 +27,8 @@ export interface BuildCmsFragmentParams {
|
|
|
26
27
|
* `isValidCmsFqn`-validated values.
|
|
27
28
|
*/
|
|
28
29
|
contentTypes?: string[] | null;
|
|
30
|
+
/** User language tag for the `language` argument; emitted only when a non-empty string. */
|
|
31
|
+
language?: string | null;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
/**
|
|
@@ -33,9 +36,10 @@ export interface BuildCmsFragmentParams {
|
|
|
33
36
|
* included only when `params.contentTypes` is a non-empty array.
|
|
34
37
|
*/
|
|
35
38
|
export function buildCmsQueryFragment(params: BuildCmsFragmentParams): QueryFragmentContribution {
|
|
36
|
-
const { keyword, uiBundleId, offset, limit, contentTypes } = params;
|
|
39
|
+
const { keyword, uiBundleId, offset, limit, contentTypes, language } = params;
|
|
37
40
|
|
|
38
41
|
const hasContentTypes = Array.isArray(contentTypes) && contentTypes.length > 0;
|
|
42
|
+
const hasLanguage = typeof language === "string" && language.length > 0;
|
|
39
43
|
|
|
40
44
|
const variableDeclarations = [
|
|
41
45
|
"$cmsKeyword: String!",
|
|
@@ -61,12 +65,19 @@ export function buildCmsQueryFragment(params: BuildCmsFragmentParams): QueryFrag
|
|
|
61
65
|
variables.cmsContentTypeFQNs = contentTypes;
|
|
62
66
|
}
|
|
63
67
|
|
|
68
|
+
// Top-level `language: String` arg, emitted only when a non-empty tag is provided.
|
|
69
|
+
const languageArg = hasLanguage ? "\n language: $cmsLanguage" : "";
|
|
70
|
+
if (hasLanguage) {
|
|
71
|
+
variableDeclarations.push("$cmsLanguage: String");
|
|
72
|
+
variables.cmsLanguage = language;
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
const fragment = `managed_content {
|
|
65
76
|
search {
|
|
66
77
|
searchContentInChannels(
|
|
67
78
|
searchIdentifiers: { uibundleIds: [$UIBundleId] }
|
|
68
79
|
keyword: $cmsKeyword
|
|
69
|
-
pagination: { limit: $cmsLimit, offset: $cmsOffset }${contentTypesArg}
|
|
80
|
+
pagination: { limit: $cmsLimit, offset: $cmsOffset }${languageArg}${contentTypesArg}
|
|
70
81
|
) {
|
|
71
82
|
total
|
|
72
83
|
offset
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { getCurrentApp } from "@salesforce/platform-sdk";
|
|
1
|
+
import { createDataSDK, getCurrentApp } from "@salesforce/platform-sdk";
|
|
2
|
+
import { fetchI18nContext } from "@salesforce/platform-sdk/i18n";
|
|
2
3
|
|
|
3
4
|
/** Returns the UIBundle id for CMS search, resolved from the running app. */
|
|
4
5
|
export async function getUIBundleId(): Promise<string> {
|
|
@@ -6,6 +7,21 @@ export async function getUIBundleId(): Promise<string> {
|
|
|
6
7
|
return app.identity?.bundleId ?? "";
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/** Fallback language tag used when the user's own language can't be resolved. */
|
|
11
|
+
export const DEFAULT_LANGUAGE = "en-US";
|
|
12
|
+
|
|
13
|
+
/** Returns the current user's language tag (e.g. `"en-US"`), falling back to {@link DEFAULT_LANGUAGE}. */
|
|
14
|
+
export async function getUserLanguage(): Promise<string> {
|
|
15
|
+
try {
|
|
16
|
+
const sdk = await createDataSDK();
|
|
17
|
+
if (!sdk?.graphql) return DEFAULT_LANGUAGE;
|
|
18
|
+
const ctx = await fetchI18nContext(sdk);
|
|
19
|
+
return ctx.lang || DEFAULT_LANGUAGE;
|
|
20
|
+
} catch {
|
|
21
|
+
return DEFAULT_LANGUAGE;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
/** Shape guard for a UIBundle record id (9YE + 12-15 alphanumerics). */
|
|
10
26
|
export const UI_BUNDLE_ID_PATTERN = /^9YE[a-zA-Z0-9]{12,15}$/;
|
|
11
27
|
|
package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/types.ts
CHANGED
|
@@ -81,6 +81,8 @@ export interface SourceRequest<T extends SourceConfig = SourceConfig> {
|
|
|
81
81
|
* synchronous. Ignored by non-CMS adapters.
|
|
82
82
|
*/
|
|
83
83
|
uiBundleId?: string;
|
|
84
|
+
/** CMS-only, optional. The current user's language tag fed to the CMS search `language` argument. */
|
|
85
|
+
language?: string;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
/**
|
|
@@ -40,6 +40,7 @@ import { FilterProvider, FilterResetButton } from "./filters/FilterContext";
|
|
|
40
40
|
import { PaginationControls } from "./controls/PaginationControls";
|
|
41
41
|
import { SortControl } from "./controls/SortControl";
|
|
42
42
|
import { resolveResultRenderer, type ResultRenderer } from "./results/resolveResultRenderer";
|
|
43
|
+
import { MIN_QUERY_LENGTH } from "../constants";
|
|
43
44
|
import { ALL_SCOPE, type SearchHandle, type SourceController } from "../types";
|
|
44
45
|
|
|
45
46
|
export interface MergedSearchResultsProps {
|
|
@@ -52,6 +53,8 @@ export interface MergedSearchResultsProps {
|
|
|
52
53
|
renderResult?: Record<string, ResultRenderer>;
|
|
53
54
|
/** Message shown when there are no results. Defaults to "No results found." */
|
|
54
55
|
emptyMessage?: string;
|
|
56
|
+
/** Message shown when the query is below {@link MIN_QUERY_LENGTH}. Defaults to "Start your search…" */
|
|
57
|
+
shortQueryMessage?: string;
|
|
55
58
|
/** Number of skeleton placeholders to show while loading. Defaults to `pageSize`. */
|
|
56
59
|
skeletonCount?: number;
|
|
57
60
|
/** Extra classes on the grid container. Defaults to a responsive 1/2/3-column grid. */
|
|
@@ -81,11 +84,16 @@ export function MergedSearchResults({
|
|
|
81
84
|
handle,
|
|
82
85
|
renderResult,
|
|
83
86
|
emptyMessage,
|
|
87
|
+
shortQueryMessage = "Start your search…",
|
|
84
88
|
skeletonCount,
|
|
85
89
|
gridClassName = DEFAULT_GRID_CLASS,
|
|
86
90
|
className,
|
|
87
91
|
}: MergedSearchResultsProps) {
|
|
88
|
-
const { globalPagination: page, mergedResults, loading, error } = handle;
|
|
92
|
+
const { globalPagination: page, mergedResults, loading, error, q } = handle;
|
|
93
|
+
// Distinguishes "haven't typed enough yet" (useSearch short-circuits below
|
|
94
|
+
// MIN_QUERY_LENGTH without fetching) from a genuine zero-match search — both
|
|
95
|
+
// otherwise present identically as loading=false, mergedResults=[].
|
|
96
|
+
const isShortQuery = q.trim().length < MIN_QUERY_LENGTH;
|
|
89
97
|
|
|
90
98
|
// A single source is in scope when the scope is narrowed (selected from the
|
|
91
99
|
// dropdown) or locked (`lockedScope` / restrictTo). Its filter chrome can
|
|
@@ -111,9 +119,11 @@ export function MergedSearchResults({
|
|
|
111
119
|
? "Searching…"
|
|
112
120
|
: error
|
|
113
121
|
? "Search failed."
|
|
114
|
-
:
|
|
115
|
-
?
|
|
116
|
-
:
|
|
122
|
+
: isShortQuery
|
|
123
|
+
? shortQueryMessage
|
|
124
|
+
: mergedResults.length === 0
|
|
125
|
+
? (emptyMessage ?? "No results found.")
|
|
126
|
+
: `${page.totalCount} result${page.totalCount === 1 ? "" : "s"}`;
|
|
117
127
|
|
|
118
128
|
return (
|
|
119
129
|
<div className={`space-y-6 ${className ?? ""}`}>
|
|
@@ -161,6 +171,8 @@ export function MergedSearchResults({
|
|
|
161
171
|
<Skeleton key={i} className="h-48 w-full rounded-lg" />
|
|
162
172
|
))}
|
|
163
173
|
</div>
|
|
174
|
+
) : isShortQuery ? (
|
|
175
|
+
<p className="py-12 text-center text-sm text-muted-foreground">{shortQueryMessage}</p>
|
|
164
176
|
) : mergedResults.length === 0 ? (
|
|
165
177
|
<p className="py-12 text-center text-sm text-muted-foreground">
|
|
166
178
|
{emptyMessage ?? "No results found."}
|
|
@@ -16,6 +16,7 @@ import { ActiveFilters } from "./filters/ActiveFilters";
|
|
|
16
16
|
import { FilterProvider, FilterResetButton } from "./filters/FilterContext";
|
|
17
17
|
import { PaginationControls } from "./controls/PaginationControls";
|
|
18
18
|
import { SortControl } from "./controls/SortControl";
|
|
19
|
+
import { MIN_QUERY_LENGTH } from "../constants";
|
|
19
20
|
import type { SourceController } from "../types";
|
|
20
21
|
|
|
21
22
|
interface SourceSectionProps {
|
|
@@ -35,6 +36,15 @@ interface SourceSectionProps {
|
|
|
35
36
|
*/
|
|
36
37
|
hideFilterChrome?: boolean;
|
|
37
38
|
emptyMessage?: string;
|
|
39
|
+
/**
|
|
40
|
+
* The current search query. Used to distinguish a not-yet-started search
|
|
41
|
+
* (query below {@link MIN_QUERY_LENGTH}, so no fetch was issued) from a
|
|
42
|
+
* genuine zero-match result, so the empty state can prompt instead of
|
|
43
|
+
* claiming "No … found."
|
|
44
|
+
*/
|
|
45
|
+
q?: string;
|
|
46
|
+
/** Message shown when the query is below {@link MIN_QUERY_LENGTH}. Defaults to "Start your search…" */
|
|
47
|
+
shortQueryMessage?: string;
|
|
38
48
|
className?: string;
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -44,6 +54,8 @@ export function SourceSection({
|
|
|
44
54
|
renderFilters,
|
|
45
55
|
hideFilterChrome,
|
|
46
56
|
emptyMessage,
|
|
57
|
+
q = "",
|
|
58
|
+
shortQueryMessage = "Start your search…",
|
|
47
59
|
className,
|
|
48
60
|
}: SourceSectionProps) {
|
|
49
61
|
const { config, result, loading, error, filters, sort, pagination } = controller;
|
|
@@ -51,6 +63,9 @@ export function SourceSection({
|
|
|
51
63
|
const totalCount = result?.totalCount;
|
|
52
64
|
const showResults = !loading && !error && nodes.length > 0;
|
|
53
65
|
const showEmpty = !loading && !error && nodes.length === 0;
|
|
66
|
+
// A query below MIN_QUERY_LENGTH never triggers a fetch, so an empty result
|
|
67
|
+
// means "not searched yet" — prompt rather than report zero matches.
|
|
68
|
+
const isShortQuery = q.trim().length < MIN_QUERY_LENGTH;
|
|
54
69
|
|
|
55
70
|
const pageSizeOptions = pagination.pageSizeOptions ?? [pagination.pageSize];
|
|
56
71
|
|
|
@@ -70,7 +85,9 @@ export function SourceSection({
|
|
|
70
85
|
: error
|
|
71
86
|
? `Failed to load ${config.label}.`
|
|
72
87
|
: showEmpty
|
|
73
|
-
?
|
|
88
|
+
? isShortQuery
|
|
89
|
+
? shortQueryMessage
|
|
90
|
+
: (emptyMessage ?? `No ${config.label.toLowerCase()} found.`)
|
|
74
91
|
: totalCount != null
|
|
75
92
|
? `${config.label}: ${totalCount} ${totalCount === 1 ? "result" : "results"}`
|
|
76
93
|
: "";
|
|
@@ -155,7 +172,9 @@ export function SourceSection({
|
|
|
155
172
|
<div className="flex flex-col items-center justify-center py-10 text-center">
|
|
156
173
|
<SearchX className="size-10 text-muted-foreground mb-3" />
|
|
157
174
|
<p className="text-sm text-muted-foreground">
|
|
158
|
-
{
|
|
175
|
+
{isShortQuery
|
|
176
|
+
? shortQueryMessage
|
|
177
|
+
: (emptyMessage ?? `No ${config.label.toLowerCase()} found.`)}
|
|
159
178
|
</p>
|
|
160
179
|
</div>
|
|
161
180
|
)}
|
|
@@ -40,7 +40,11 @@ import { MIN_QUERY_LENGTH } from "../constants";
|
|
|
40
40
|
// `resolveChannelId` derives the discovery channel id from a CMS result;
|
|
41
41
|
// `useSearchableContentTypes` is the session-cached discovery hook;
|
|
42
42
|
// `isValidCmsFqn` validates a selected FQN before it enters state or a query.
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
getUIBundleId,
|
|
45
|
+
getUserLanguage,
|
|
46
|
+
isConfiguredUIBundleId,
|
|
47
|
+
} from "../adapters/cms/searchChannel";
|
|
44
48
|
import { getOrgSupportsCmsSearch } from "../adapters/cms/api/orgApiVersionService";
|
|
45
49
|
import { resolveChannelId } from "../adapters/cms/channelResolver";
|
|
46
50
|
import { useSearchableContentTypes } from "../adapters/cms/hooks/useSearchableContentTypes";
|
|
@@ -503,8 +507,8 @@ export function useSearch(config: SearchConfig, options?: UseSearchOptions): Sea
|
|
|
503
507
|
// page-level error — content-type narrowing is an enhancement, not a
|
|
504
508
|
// hard requirement. Read in `.catch` below.
|
|
505
509
|
let isDiscoveryRefetch = false;
|
|
506
|
-
Promise.all([getUIBundleId(), getOrgSupportsCmsSearch()])
|
|
507
|
-
.then(([uiBundleId, orgSupportsCmsSearch]) => {
|
|
510
|
+
Promise.all([getUIBundleId(), getUserLanguage(), getOrgSupportsCmsSearch()])
|
|
511
|
+
.then(([uiBundleId, userLanguage, orgSupportsCmsSearch]) => {
|
|
508
512
|
if (cancelled || generation !== fetchGenRef.current) return;
|
|
509
513
|
|
|
510
514
|
const requests: SourceRequest[] = activeSources.flatMap((source) => {
|
|
@@ -541,7 +545,7 @@ export function useSearch(config: SearchConfig, options?: UseSearchOptions): Sea
|
|
|
541
545
|
if (!cmsFqn && discoveredContentTypeFqns.length > 0) {
|
|
542
546
|
isDiscoveryRefetch = true;
|
|
543
547
|
}
|
|
544
|
-
return [{ ...base, uiBundleId, contentTypes }];
|
|
548
|
+
return [{ ...base, uiBundleId, language: userLanguage, contentTypes }];
|
|
545
549
|
});
|
|
546
550
|
|
|
547
551
|
// Every in-scope source was gated out (e.g. a CMS-only scope while CMS
|
package/dist/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/ui-bundle-template-base-sfdx-project",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.66.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@salesforce/ui-bundle-template-base-sfdx-project",
|
|
9
|
-
"version": "11.
|
|
9
|
+
"version": "11.66.2",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"fast-xml-parser": "^5.9.3",
|
package/dist/package.json
CHANGED