@salesforce/ui-bundle-template-app-react-template-b2e 12.8.0 → 12.9.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 CHANGED
@@ -3,6 +3,14 @@
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
+ ## [12.9.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v12.8.0...v12.9.0) (2026-09-29)
7
+
8
+ **Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
9
+
10
+
11
+
12
+
13
+
6
14
  ## [12.8.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v12.7.0...v12.8.0) (2026-09-23)
7
15
 
8
16
  **Note:** Version bump only for package @salesforce/ui-bundle-template-base-sfdx-project
@@ -19,8 +19,8 @@
19
19
  "graphql:schema": "node scripts/get-graphql-schema.mjs"
20
20
  },
21
21
  "dependencies": {
22
- "@salesforce/platform-sdk": "^12.8.0",
23
- "@salesforce/ui-bundle": "^12.8.0",
22
+ "@salesforce/platform-sdk": "^12.9.0",
23
+ "@salesforce/ui-bundle": "^12.9.0",
24
24
  "@tailwindcss/vite": "^4.1.17",
25
25
  "class-variance-authority": "^0.7.1",
26
26
  "clsx": "^2.1.1",
@@ -46,8 +46,8 @@
46
46
  "@graphql-eslint/eslint-plugin": "^4.1.0",
47
47
  "@graphql-tools/utils": "^11.0.0",
48
48
  "@playwright/test": "^1.49.0",
49
- "@salesforce/graphiti": "^12.8.0",
50
- "@salesforce/vite-plugin-ui-bundle": "^12.8.0",
49
+ "@salesforce/graphiti": "^12.9.0",
50
+ "@salesforce/vite-plugin-ui-bundle": "^12.9.0",
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.1.0",
53
53
  "@testing-library/user-event": "^14.5.2",
@@ -107,11 +107,7 @@ export function PaginationControls({
107
107
  <Label htmlFor={selectId} className="text-sm font-normal whitespace-nowrap">
108
108
  Results per page:
109
109
  </Label>
110
- <Select
111
- value={pageSize.toString()}
112
- onValueChange={handlePageSizeChange}
113
- disabled={disabled}
114
- >
110
+ <Select value={pageSize.toString()} onValueChange={handlePageSizeChange}>
115
111
  <SelectTrigger id={selectId} className="w-16">
116
112
  <SelectValue />
117
113
  </SelectTrigger>
@@ -20,6 +20,11 @@ import { Link } from "react-router";
20
20
  import type { CmsSourceConfig, CmsSearchItem } from "../../adapters/cms/types";
21
21
  import { formatContentTypeLabel, isValidCmsFqn } from "../../adapters/cms/contentTypeUtils";
22
22
 
23
+ const EM_BLOCK_SPLIT = /(<(?:em)>[\s\S]*?<\/(?:em)>)/g;
24
+ const EM_BLOCK_EXEC = /^<(?:em)>([\s\S]*?)<\/(?:em)>$/;
25
+ const STRAY_TAG = /<\/?[a-zA-Z][^>]*>/g;
26
+ const ROUTE_TOKEN = /:([A-Za-z_][A-Za-z0-9_]*)/g;
27
+
23
28
  /** Formats an ISO date string for display; echoes the raw string on failure. */
24
29
  function formatPublishedDate(dateStr: string): string {
25
30
  const date = new Date(dateStr);
@@ -43,7 +48,7 @@ function resolveRoute(pattern: string, item: CmsSearchItem): string | null {
43
48
  contentType: item.contentType,
44
49
  };
45
50
  let failed = false;
46
- const out = pattern.replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, (_match, token: string) => {
51
+ const out = pattern.replace(ROUTE_TOKEN, (_match, token: string) => {
47
52
  const value = tokens[token];
48
53
  if (value == null || value === "") {
49
54
  failed = true;
@@ -54,6 +59,44 @@ function resolveRoute(pattern: string, item: CmsSearchItem): string | null {
54
59
  return failed ? null : out;
55
60
  }
56
61
 
62
+ /**
63
+ * - Decodes every HTML entity the browser knows (named + numeric).
64
+ * - Runs twice to unwrap the double-encoding CMS search sometimes returns.
65
+ */
66
+ function decodeEntities(input: string): string {
67
+ if (typeof document === "undefined") return input;
68
+ const el = document.createElement("textarea");
69
+ const pass = (s: string) => {
70
+ el.innerHTML = s;
71
+ return el.value;
72
+ };
73
+ return pass(pass(input));
74
+ }
75
+
76
+ /**
77
+ * Parses a snippet that may contain `<em>…</em>` segments and renders it as highlighted text,
78
+ * and strips any other HTML tags so their content renders as plain text.
79
+ */
80
+ function renderHighlightedSnippet(snippet: string): ReactNode[] {
81
+ const decoded = decodeEntities(snippet);
82
+ const parts = decoded.split(EM_BLOCK_SPLIT);
83
+ return parts.map((part, i) => {
84
+ const emphasisMatch = EM_BLOCK_EXEC.exec(part);
85
+ if (emphasisMatch) {
86
+ return (
87
+ <mark key={i} className="bg-yellow-200 dark:bg-yellow-700/60 rounded-sm px-0.5">
88
+ {emphasisMatch[1].replace(STRAY_TAG, "")}
89
+ </mark>
90
+ );
91
+ }
92
+ return part.replace(STRAY_TAG, "");
93
+ });
94
+ }
95
+
96
+ function toPlainText(raw: string): string {
97
+ return decodeEntities(raw).replace(STRAY_TAG, "");
98
+ }
99
+
57
100
  interface CmsResultRowProps {
58
101
  node: unknown;
59
102
  source: CmsSourceConfig;
@@ -71,9 +114,11 @@ export function CmsResultRow({ node, source }: CmsResultRowProps) {
71
114
 
72
115
  const content: ReactNode = (
73
116
  <>
74
- <span className="font-medium">{item.title ?? "—"}</span>
117
+ <span className="font-medium">{item.title ? renderHighlightedSnippet(item.title) : "—"}</span>
75
118
  {item.highlightedSnippet && (
76
- <p className="text-sm text-neutral-600 dark:text-neutral-300">{item.highlightedSnippet}</p>
119
+ <p className="text-sm text-neutral-600 dark:text-neutral-300">
120
+ {renderHighlightedSnippet(item.highlightedSnippet)}
121
+ </p>
77
122
  )}
78
123
  {subtitleParts.length > 0 && (
79
124
  <p className="text-sm text-neutral-600 dark:text-neutral-300">
@@ -92,7 +137,7 @@ export function CmsResultRow({ node, source }: CmsResultRowProps) {
92
137
  <Link
93
138
  to={href}
94
139
  className="block hover:bg-accent rounded-md px-3 -mx-3 py-1 transition-colors"
95
- aria-label={`View content: ${item.title ?? "content"}`}
140
+ aria-label={`View content: ${item.title ? toPlainText(item.title) : "content"}`}
96
141
  >
97
142
  {content}
98
143
  </Link>
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "12.8.0",
3
+ "version": "12.9.0",
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": "12.8.0",
9
+ "version": "12.9.0",
10
10
  "license": "SEE LICENSE IN LICENSE.txt",
11
11
  "dependencies": {
12
12
  "fast-xml-parser": "^5.9.3",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "12.8.0",
3
+ "version": "12.9.0",
4
4
  "description": "Base SFDX project template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-app-react-template-b2e",
3
- "version": "12.8.0",
3
+ "version": "12.9.0",
4
4
  "description": "Salesforce React internal app template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",