@salesforce/ui-bundle-template-app-react-sample-b2e 11.55.1 → 11.56.0
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 +8 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/package.json +4 -4
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/README.md +235 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/__tests__/queryBuilder.test.ts +166 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/channelResolver.test.ts +73 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/cmsQueryFragment.test.ts +127 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/contentTypeSessionCache.test.ts +114 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/contentTypeUtils.test.ts +82 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/orgApiVersionService.test.ts +57 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/parseResponse.test.ts +102 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/searchChannel.test.ts +98 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/__tests__/searchableContentTypesService.test.ts +153 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/api/apiUtils.ts +56 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/api/orgApiVersionService.ts +26 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/api/searchableContentTypesService.ts +116 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/channelResolver.ts +40 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/cmsQueryFragment.ts +97 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/contentTypeSessionCache.ts +134 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/contentTypeUtils.ts +38 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/hooks/useSearchableContentTypes.ts +128 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/index.ts +54 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/parseResponse.ts +65 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/searchChannel.ts +15 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/cms/types.ts +66 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/registry.ts +38 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/sobject/index.ts +19 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/sobject/parseResponse.ts +56 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/sobject/queryFragment.ts +141 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/adapters/types.ts +101 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/api/__tests__/searchService.test.ts +304 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/api/searchService.ts +110 -45
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/GlobalSearchBox.tsx +98 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/MergedSearchResults.tsx +34 -17
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/Search.tsx +31 -13
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/SearchResults.tsx +17 -11
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/SourceSection.tsx +9 -4
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/__tests__/Search.test.tsx +173 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/controls/ScopeSelector.tsx +50 -6
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/results/CmsResultRow.tsx +101 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/results/__tests__/CmsResultRow.test.tsx +139 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/results/__tests__/resolveResultRenderer.test.ts +104 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/components/results/resolveResultRenderer.ts +49 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/config.json +7 -2
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/constants.ts +8 -0
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/hooks/useSearch.ts +313 -50
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/index.ts +26 -1
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/queryBuilder.ts +62 -118
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/types.ts +74 -5
- package/dist/force-app/main/default/uiBundles/propertymanagementapp/tsconfig.tsbuildinfo +1 -1
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from "vitest";
|
|
8
|
+
import { render } from "@testing-library/react";
|
|
9
|
+
import { MemoryRouter } from "react-router";
|
|
10
|
+
import { CmsResultRow } from "../CmsResultRow";
|
|
11
|
+
import type { CmsSearchItem, CmsSourceConfig } from "../../../adapters/cms/types";
|
|
12
|
+
|
|
13
|
+
function renderWithRouter(
|
|
14
|
+
ui: React.ReactElement,
|
|
15
|
+
{ initialEntries = ["/"] }: { initialEntries?: string[] } = {},
|
|
16
|
+
) {
|
|
17
|
+
const { container } = render(<MemoryRouter initialEntries={initialEntries}>{ui}</MemoryRouter>);
|
|
18
|
+
return container;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const source: CmsSourceConfig = {
|
|
22
|
+
kind: "cms",
|
|
23
|
+
key: "content",
|
|
24
|
+
label: "Content",
|
|
25
|
+
routePattern: "/content/:contentType/:id",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const baseItem: CmsSearchItem = {
|
|
29
|
+
managedContentId: "mc001",
|
|
30
|
+
managedContentKey: "article-key",
|
|
31
|
+
title: "Test Article",
|
|
32
|
+
contentType: "sfdc_cms__news",
|
|
33
|
+
language: "en_US",
|
|
34
|
+
managedContentChannelDeliveryDetails: [
|
|
35
|
+
{
|
|
36
|
+
contentUrl: "/content/test",
|
|
37
|
+
publishedDate: "2026-01-15T10:00:00Z",
|
|
38
|
+
managedContentChannelDetails: {
|
|
39
|
+
id: "ch001",
|
|
40
|
+
name: "Test Channel",
|
|
41
|
+
type: "public",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
describe("CmsResultRow", () => {
|
|
48
|
+
describe("snippet rendering", () => {
|
|
49
|
+
it("renders the snippet text as plain muted text when highlightedSnippet present", () => {
|
|
50
|
+
const item: CmsSearchItem = {
|
|
51
|
+
...baseItem,
|
|
52
|
+
highlightedSnippet: "This article covers annual reports and financial data.",
|
|
53
|
+
};
|
|
54
|
+
const container = renderWithRouter(<CmsResultRow node={item} source={source} />);
|
|
55
|
+
|
|
56
|
+
expect(container.textContent).toContain(
|
|
57
|
+
"This article covers annual reports and financial data.",
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("renders the snippet with the same muted styling as the subtitle (no mark element)", () => {
|
|
62
|
+
const item: CmsSearchItem = {
|
|
63
|
+
...baseItem,
|
|
64
|
+
highlightedSnippet: "This article covers annual reports and financial data.",
|
|
65
|
+
};
|
|
66
|
+
const container = renderWithRouter(<CmsResultRow node={item} source={source} />);
|
|
67
|
+
|
|
68
|
+
expect(container.querySelector("mark")).toBeFalsy();
|
|
69
|
+
const snippet = Array.from(container.querySelectorAll("p")).find((p) =>
|
|
70
|
+
p.textContent?.includes("This article covers"),
|
|
71
|
+
);
|
|
72
|
+
expect(snippet).toBeTruthy();
|
|
73
|
+
expect(snippet?.className).toContain("text-muted-foreground");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("renders the snippet regardless of any q param", () => {
|
|
77
|
+
const item: CmsSearchItem = {
|
|
78
|
+
...baseItem,
|
|
79
|
+
highlightedSnippet: "This article covers annual reports and financial data.",
|
|
80
|
+
};
|
|
81
|
+
const container = renderWithRouter(<CmsResultRow node={item} source={source} />, {
|
|
82
|
+
initialEntries: ["/"],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(container.textContent).toContain("This article covers");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("does not render snippet element when highlightedSnippet is absent", () => {
|
|
89
|
+
const item: CmsSearchItem = {
|
|
90
|
+
...baseItem,
|
|
91
|
+
};
|
|
92
|
+
const container = renderWithRouter(<CmsResultRow node={item} source={source} />);
|
|
93
|
+
|
|
94
|
+
expect(container.textContent).toContain("Test Article");
|
|
95
|
+
expect(container.querySelector("mark")).toBeFalsy();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("does not render snippet element when highlightedSnippet is empty string", () => {
|
|
99
|
+
const item: CmsSearchItem = {
|
|
100
|
+
...baseItem,
|
|
101
|
+
highlightedSnippet: "",
|
|
102
|
+
};
|
|
103
|
+
const container = renderWithRouter(<CmsResultRow node={item} source={source} />);
|
|
104
|
+
|
|
105
|
+
expect(container.textContent).toContain("Test Article");
|
|
106
|
+
expect(container.querySelector("mark")).toBeFalsy();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("link rendering", () => {
|
|
111
|
+
it("renders as a link when routePattern is configured", () => {
|
|
112
|
+
const container = renderWithRouter(<CmsResultRow node={baseItem} source={source} />, {
|
|
113
|
+
initialEntries: ["/"],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const link = container.querySelector("a");
|
|
117
|
+
expect(link).toBeTruthy();
|
|
118
|
+
expect(link?.getAttribute("href")).toBe("/content/sfdc_cms__news/mc001");
|
|
119
|
+
expect(link?.getAttribute("aria-label")).toBe("View content: Test Article");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("renders as plain div when routePattern is not configured", () => {
|
|
123
|
+
const sourceWithoutRoute: CmsSourceConfig = {
|
|
124
|
+
kind: "cms",
|
|
125
|
+
key: "content",
|
|
126
|
+
label: "Content",
|
|
127
|
+
};
|
|
128
|
+
const container = renderWithRouter(
|
|
129
|
+
<CmsResultRow node={baseItem} source={sourceWithoutRoute} />,
|
|
130
|
+
{
|
|
131
|
+
initialEntries: ["/"],
|
|
132
|
+
},
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
expect(container.querySelector("a")).toBeFalsy();
|
|
136
|
+
expect(container.textContent).toContain("Test Article");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Tests for resolveResultRenderer precedence:
|
|
9
|
+
* 1. renderResult[key] override > 2. kind==="cms"→CmsResultRow > 3. DefaultResultRow
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from "vitest";
|
|
12
|
+
import { resolveResultRenderer } from "../resolveResultRenderer";
|
|
13
|
+
import type { SObjectSourceConfig, CmsSourceConfig } from "../../../types";
|
|
14
|
+
|
|
15
|
+
const sobjectSource: SObjectSourceConfig = {
|
|
16
|
+
kind: "sobject",
|
|
17
|
+
key: "accounts",
|
|
18
|
+
label: "Accounts",
|
|
19
|
+
objectName: "Account",
|
|
20
|
+
displayFields: ["Name"],
|
|
21
|
+
searchableFields: ["Name"],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const cmsSource: CmsSourceConfig = {
|
|
25
|
+
kind: "cms",
|
|
26
|
+
key: "content",
|
|
27
|
+
label: "Content",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe("resolveResultRenderer", () => {
|
|
31
|
+
it("SObject source with no override returns DefaultResultRow renderer", () => {
|
|
32
|
+
const renderer = resolveResultRenderer(sobjectSource);
|
|
33
|
+
expect(renderer).toBeTypeOf("function");
|
|
34
|
+
// Should render without throwing
|
|
35
|
+
const node = { Id: "001", Name: "Acme" };
|
|
36
|
+
const result = (renderer as (node: unknown) => unknown)(node);
|
|
37
|
+
expect(result).toBeTruthy();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("CMS source with no override returns CmsResultRow renderer", () => {
|
|
41
|
+
const renderer = resolveResultRenderer(cmsSource);
|
|
42
|
+
expect(renderer).toBeTypeOf("function");
|
|
43
|
+
// Should render without throwing
|
|
44
|
+
const node = { managedContentId: "mc1", title: "Report" };
|
|
45
|
+
const result = (renderer as (node: unknown) => unknown)(node);
|
|
46
|
+
expect(result).toBeTruthy();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("SObject renderer carries a DevTools displayName", () => {
|
|
50
|
+
const renderer = resolveResultRenderer(sobjectSource);
|
|
51
|
+
expect((renderer as { displayName?: string }).displayName).toBe("DefaultResultRowRenderer");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("CMS renderer carries a DevTools displayName", () => {
|
|
55
|
+
const renderer = resolveResultRenderer(cmsSource);
|
|
56
|
+
expect((renderer as { displayName?: string }).displayName).toBe("CmsResultRowRenderer");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("renderResult[key] override wins for SObject source (function override)", () => {
|
|
60
|
+
const customRenderer = (node: unknown) => `Custom: ${JSON.stringify(node)}`;
|
|
61
|
+
const overrides = { accounts: customRenderer };
|
|
62
|
+
|
|
63
|
+
const renderer = resolveResultRenderer(sobjectSource, overrides);
|
|
64
|
+
expect(renderer).toBe(customRenderer);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("renderResult[key] override wins for CMS source (function override)", () => {
|
|
68
|
+
const customRenderer = (node: unknown) => `Custom CMS: ${JSON.stringify(node)}`;
|
|
69
|
+
const overrides = { content: customRenderer };
|
|
70
|
+
|
|
71
|
+
const renderer = resolveResultRenderer(cmsSource, overrides);
|
|
72
|
+
expect(renderer).toBe(customRenderer);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("renderResult[key] = false hides the source (returns false)", () => {
|
|
76
|
+
const overrides = { accounts: false as const };
|
|
77
|
+
const renderer = resolveResultRenderer(sobjectSource, overrides);
|
|
78
|
+
expect(renderer).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("renderResult[key] = false hides CMS source (returns false)", () => {
|
|
82
|
+
const overrides = { content: false as const };
|
|
83
|
+
const renderer = resolveResultRenderer(cmsSource, overrides);
|
|
84
|
+
expect(renderer).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("other keys in renderResult do not affect the source without a matching key", () => {
|
|
88
|
+
const overrides = { someOtherKey: false as const };
|
|
89
|
+
|
|
90
|
+
const sobjectRenderer = resolveResultRenderer(sobjectSource, overrides);
|
|
91
|
+
expect(sobjectRenderer).toBeTypeOf("function");
|
|
92
|
+
|
|
93
|
+
const cmsRenderer = resolveResultRenderer(cmsSource, overrides);
|
|
94
|
+
expect(cmsRenderer).toBeTypeOf("function");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("undefined renderResult is the same as no overrides", () => {
|
|
98
|
+
const renderer1 = resolveResultRenderer(sobjectSource, undefined);
|
|
99
|
+
const renderer2 = resolveResultRenderer(sobjectSource);
|
|
100
|
+
// Both should return functions (DefaultResultRow)
|
|
101
|
+
expect(renderer1).toBeTypeOf("function");
|
|
102
|
+
expect(renderer2).toBeTypeOf("function");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for choosing how one source's result node is rendered.
|
|
3
|
+
*
|
|
4
|
+
* Every result layout (`SearchResults`, `MergedSearchResults`, and any future
|
|
5
|
+
* grid/list) MUST resolve its per-node renderer through this helper so the
|
|
6
|
+
* kind-aware default can never be missed in one mode. The rule, in order:
|
|
7
|
+
*
|
|
8
|
+
* 1. A `renderResult[source.key]` config override wins in EVERY mode —
|
|
9
|
+
* including `false`, which hides the source entirely.
|
|
10
|
+
* 2. Otherwise `source.kind === "cms"` → {@link CmsResultRow}.
|
|
11
|
+
* 3. Otherwise (SObject) → {@link DefaultResultRow}.
|
|
12
|
+
*
|
|
13
|
+
* Returning the same union the callers already use (`renderer | false`) lets a
|
|
14
|
+
* component early-return `null` for a hidden source without special-casing.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { createElement, type ReactNode } from "react";
|
|
18
|
+
import type { SourceConfig } from "../../types";
|
|
19
|
+
import { DefaultResultRow } from "./DefaultResultRow";
|
|
20
|
+
import { CmsResultRow } from "./CmsResultRow";
|
|
21
|
+
|
|
22
|
+
/** A per-node renderer, or `false` to hide the source's results. */
|
|
23
|
+
export type ResultRenderer = ((node: unknown) => ReactNode) | false;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the renderer for one source. See the module doc for the precedence
|
|
27
|
+
* rules. `renderResult` is the optional per-source override map from `<Search>`.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveResultRenderer(
|
|
30
|
+
source: SourceConfig,
|
|
31
|
+
renderResult?: Record<string, ResultRenderer>,
|
|
32
|
+
): ResultRenderer {
|
|
33
|
+
const override = renderResult?.[source.key];
|
|
34
|
+
// A config override wins in every mode — including `false` (hide).
|
|
35
|
+
if (override !== undefined) return override;
|
|
36
|
+
|
|
37
|
+
// Assign a `displayName` so React DevTools shows a readable label for the
|
|
38
|
+
// per-node component factory and the `react/display-name` lint rule is
|
|
39
|
+
// satisfied. (This is a debug label only — it does not affect reference
|
|
40
|
+
// identity; a new function is still created on each call.)
|
|
41
|
+
if (source.kind === "cms") {
|
|
42
|
+
const renderCmsRow = (node: unknown) => createElement(CmsResultRow, { node, source });
|
|
43
|
+
renderCmsRow.displayName = "CmsResultRowRenderer";
|
|
44
|
+
return renderCmsRow;
|
|
45
|
+
}
|
|
46
|
+
const renderDefaultRow = (node: unknown) => createElement(DefaultResultRow, { node, source });
|
|
47
|
+
renderDefaultRow.displayName = "DefaultResultRowRenderer";
|
|
48
|
+
return renderDefaultRow;
|
|
49
|
+
}
|
package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/config.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"pagination": {
|
|
3
|
+
"mode": "per-source",
|
|
3
4
|
"pageSize": 10,
|
|
4
5
|
"pageSizeOptions": [5, 10, 20]
|
|
5
6
|
},
|
|
@@ -38,7 +39,6 @@
|
|
|
38
39
|
"objectName": "Contact",
|
|
39
40
|
"label": "Contacts",
|
|
40
41
|
"labelSingular": "Contact",
|
|
41
|
-
"routePattern": "/contacts/:id",
|
|
42
42
|
"searchableFields": ["Name", "Email", "Phone"],
|
|
43
43
|
"displayFields": [
|
|
44
44
|
"Name",
|
|
@@ -58,7 +58,6 @@
|
|
|
58
58
|
"objectName": "Opportunity",
|
|
59
59
|
"label": "Opportunities",
|
|
60
60
|
"labelSingular": "Opportunity",
|
|
61
|
-
"routePattern": "/opportunities/:id",
|
|
62
61
|
"searchableFields": ["Name"],
|
|
63
62
|
"displayFields": [
|
|
64
63
|
"Name",
|
|
@@ -78,6 +77,12 @@
|
|
|
78
77
|
{ "field": "Name", "label": "Name" }
|
|
79
78
|
],
|
|
80
79
|
"defaultSort": { "field": "CloseDate", "direction": "DESC" }
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"kind": "cms",
|
|
83
|
+
"key": "content",
|
|
84
|
+
"label": "Content",
|
|
85
|
+
"labelSingular": "Content"
|
|
81
86
|
}
|
|
82
87
|
]
|
|
83
88
|
}
|
package/dist/force-app/main/default/uiBundles/propertymanagementapp/src/features/search/constants.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimum trimmed keyword length before a search fires. Both the SObject and
|
|
3
|
+
* CMS search backends reject terms shorter than this (returning errors that the
|
|
4
|
+
* UI would otherwise render as "Search failed for this source." on every
|
|
5
|
+
* source), so the query is gated client-side until the user types at least this
|
|
6
|
+
* many characters. Shared so every `<Search>` consumer inherits the same floor.
|
|
7
|
+
*/
|
|
8
|
+
export const MIN_QUERY_LENGTH = 3;
|