@salesforce/webapp-template-feature-react-chart-experimental 1.26.0 → 1.27.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.
@@ -123,7 +123,8 @@ mutation mutateEntityName(
123
123
  ## Mutation Standalone (Default) Output Format - CLEAN CODE ONLY
124
124
 
125
125
  ```javascript
126
- const QUERY_NAME = `
126
+ import { gql } from 'api/graphql.ts';
127
+ const QUERY_NAME = gql`
127
128
  mutation mutateEntity($input: EntityNameOperationInput!) {
128
129
  uiapi {
129
130
  EntityNameOperation(input: $input) {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: install-feature
3
- description: Install a feature into the current app. Use when the user asks to add a feature, capability, or functionality like authentication, search, charts, or navigation to their React web app.
3
+ description: Install a feature into the current app. Use when the user asks to add a feature, capability, or functionality like authentication, search, charts, or navigation to their React web app. Do this when user wants to do _anything_ to webapplications/webapps.
4
4
  ---
5
5
 
6
6
  # Install feature
@@ -17,10 +17,12 @@ Available features (npm packages):
17
17
  | **Authentication** | `@salesforce/webapp-template-feature-react-authentication-experimental` | Login, register, password reset, protected routes |
18
18
  | **Global search** | `@salesforce/webapp-template-feature-react-global-search-experimental` | Search Salesforce objects with filters and pagination |
19
19
  | **Navigation menu** | `@salesforce/webapp-template-feature-react-nav-menu-experimental` | App layout with responsive navigation menu |
20
- | **Analytics charts** | `@salesforce/webapp-template-feature-react-chart-experimental` | Recharts line/bar charts with theming (AnalyticsChart, ChartContainer) |
20
+ | **Analytics charts** | `@salesforce/webapp-template-feature-react-chart-experimental` | Recharts line/bar charts with theming (AnalyticsChart, ChartContainer) |
21
+ | **GraphQL data access**| `@salesforce/webapp-template-feature-graphql-experimental` | executeGraphQL utilities, codegen tooling, and example AccountsTable |
21
22
  | **Shared UI (shadcn)** | `@salesforce/webapp-template-feature-react-shadcn-experimental` | Button, Card, Input, Select, Table, Tabs, etc. |
22
23
 
23
24
 
25
+
24
26
  If no feature matches, tell the user and offer to build it from scratch following the project's existing patterns.
25
27
 
26
28
  ## 2. Install the npm package
@@ -31,12 +33,18 @@ npm install <package-name>
31
33
 
32
34
  ## 3. Copy skills and rules from the package
33
35
 
34
- After install, check the package in `node_modules/<package-name>/` for:
36
+ Run the script so skills and rules from the package are copied into your project. Pass `[skills-dir]` and `[rules-dir]` for your environment; the script reports what it copied.
37
+
38
+ ```bash
39
+ # Default (e.g. Cline): .cline/skills, .clinerules
40
+ bash <this-skill>/scripts/copy-feature-assets.sh <package-name>
35
41
 
36
- - `**skills/**` If it exists, copy each skill folder into the current project's skills directory (e.g. `.cline/skills/` or `.cursor/skills/`).
37
- - `**rules/**` – If it exists, copy each rule file into the current project's rules directory (e.g. `.clinerules/` or `.cursor/rules/`).
42
+ # Agentforce: .a4drules/ ([docs](https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/devagent-rules.html))
43
+ bash <this-skill>/scripts/copy-feature-assets.sh <package-name> .a4drules/skills .a4drules
38
44
 
39
- Not every package has skills or rules; skip this step if neither directory exists.
45
+ # Cursor: .cursor/skills, .cursor/rules
46
+ bash <this-skill>/scripts/copy-feature-assets.sh <package-name> .cursor/skills .cursor/rules
47
+ ```
40
48
 
41
49
  ## 4. Read the feature's exports and components
42
50
 
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # Copies skills/ and rules/ from an installed npm package into the current project.
3
+ # Usage: bash <this-script> <package-name> [skills-dir] [rules-dir]
4
+ #
5
+ # Examples:
6
+ # bash copy-feature-assets.sh @salesforce/webapp-template-feature-graphql-experimental
7
+ # bash copy-feature-assets.sh @salesforce/webapp-template-feature-graphql-experimental .cursor/skills .cursor/rules
8
+ set -euo pipefail
9
+
10
+ PKG="${1:?Usage: copy-feature-assets.sh <package-name> [skills-dir] [rules-dir]}"
11
+ SKILLS_DIR="${2:-.cline/skills}"
12
+ RULES_DIR="${3:-.clinerules}"
13
+
14
+ PKG_DIR="node_modules/$PKG"
15
+
16
+ if [ ! -d "$PKG_DIR" ]; then
17
+ echo "Error: Package not found at $PKG_DIR" >&2
18
+ echo "Run 'npm install $PKG' first." >&2
19
+ exit 1
20
+ fi
21
+
22
+ if [ -d "$PKG_DIR/skills" ] && [ "$(ls -A "$PKG_DIR/skills")" ]; then
23
+ mkdir -p "$SKILLS_DIR"
24
+ cp -r "$PKG_DIR"/skills/* "$SKILLS_DIR"/
25
+ echo "Copied skills → $SKILLS_DIR/"
26
+ else
27
+ echo "No skills/ found in $PKG_DIR — nothing to copy."
28
+ fi
29
+
30
+ if [ -d "$PKG_DIR/rules" ] && [ "$(ls -A "$PKG_DIR/rules")" ]; then
31
+ mkdir -p "$RULES_DIR"
32
+ cp -r "$PKG_DIR"/rules/* "$RULES_DIR"/
33
+ echo "Copied rules → $RULES_DIR/"
34
+ else
35
+ echo "No rules/ found in $PKG_DIR — nothing to copy."
36
+ fi
package/dist/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
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.27.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.26.0...v1.27.0) (2026-02-12)
7
+
8
+
9
+ ### Features
10
+
11
+ * defining graphql feature @W-20226218@ ([#83](https://github.com/salesforce-experience-platform-emu/webapps/issues/83)) ([51a9273](https://github.com/salesforce-experience-platform-emu/webapps/commit/51a92733063e1adf0c6e44ceab37162baddc2ffb))
12
+
13
+
14
+
15
+
16
+
6
17
  # [1.26.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.25.2...v1.26.0) (2026-02-11)
7
18
 
8
19
  **Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
@@ -1,9 +1,11 @@
1
- import { Outlet } from 'react-router';
1
+ import { Outlet } from "react-router";
2
+ import NavigationMenu from "./navigationMenu";
2
3
 
3
4
  export default function AppLayout() {
4
- return (
5
- <>
6
- <Outlet />
7
- </>
8
- );
5
+ return (
6
+ <>
7
+ <NavigationMenu />
8
+ <Outlet />
9
+ </>
10
+ );
9
11
  }
@@ -0,0 +1,81 @@
1
+ import { Link, useLocation } from "react-router";
2
+ import { getAllRoutes } from "./router-utils";
3
+ import { useState } from "react";
4
+
5
+ export default function NavigationMenu() {
6
+ const [isOpen, setIsOpen] = useState(false);
7
+ const location = useLocation();
8
+
9
+ const isActive = (path: string) => location.pathname === path;
10
+
11
+ const toggleMenu = () => setIsOpen(!isOpen);
12
+
13
+ // Get all route configs and filter by showInNavigation
14
+ const navigationRoutes: { path: string; label: string }[] = getAllRoutes()
15
+ .filter(
16
+ (route) =>
17
+ route.handle?.showInNavigation === true &&
18
+ route.fullPath !== undefined &&
19
+ route.handle?.label !== undefined,
20
+ )
21
+ .map(
22
+ (route) =>
23
+ ({
24
+ path: route.fullPath,
25
+ label: route.handle?.label,
26
+ }) as { path: string; label: string },
27
+ );
28
+
29
+ return (
30
+ <nav className="bg-white border-b border-gray-200">
31
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
32
+ <div className="flex justify-between items-center h-16">
33
+ <Link to="/" className="text-xl font-semibold text-gray-900">
34
+ React App
35
+ </Link>
36
+ <button
37
+ onClick={toggleMenu}
38
+ className="p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
39
+ aria-label="Toggle menu"
40
+ >
41
+ <div className="w-6 h-6 flex flex-col justify-center space-y-1.5">
42
+ <span
43
+ className={`block h-0.5 w-6 bg-current transition-all ${
44
+ isOpen ? "rotate-45 translate-y-2" : ""
45
+ }`}
46
+ />
47
+ <span
48
+ className={`block h-0.5 w-6 bg-current transition-all ${isOpen ? "opacity-0" : ""}`}
49
+ />
50
+ <span
51
+ className={`block h-0.5 w-6 bg-current transition-all ${
52
+ isOpen ? "-rotate-45 -translate-y-2" : ""
53
+ }`}
54
+ />
55
+ </div>
56
+ </button>
57
+ </div>
58
+ {isOpen && (
59
+ <div className="pb-4">
60
+ <div className="flex flex-col space-y-2">
61
+ {navigationRoutes.map((item) => (
62
+ <Link
63
+ key={item.path}
64
+ to={item.path}
65
+ onClick={() => setIsOpen(false)}
66
+ className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
67
+ isActive(item.path)
68
+ ? "bg-blue-100 text-blue-700"
69
+ : "text-gray-700 hover:bg-gray-100"
70
+ }`}
71
+ >
72
+ {item.label}
73
+ </Link>
74
+ ))}
75
+ </div>
76
+ </div>
77
+ )}
78
+ </div>
79
+ </nav>
80
+ );
81
+ }
@@ -0,0 +1,10 @@
1
+ export default function About() {
2
+ return (
3
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
4
+ <div className="text-center">
5
+ <h1 className="text-4xl font-bold text-gray-900 mb-4">About</h1>
6
+ <p className="text-lg text-gray-600">This is the about page.</p>
7
+ </div>
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,10 @@
1
+ export default function New() {
2
+ return (
3
+ <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
4
+ <div className="text-center">
5
+ <h1 className="text-4xl font-bold text-gray-900 mb-4">New</h1>
6
+ <p className="text-lg text-gray-600">This is the new page.</p>
7
+ </div>
8
+ </div>
9
+ );
10
+ }
@@ -0,0 +1,34 @@
1
+ import type { RouteObject } from "react-router";
2
+ import { routes } from "./routes";
3
+
4
+ export type RouteWithFullPath = RouteObject & { fullPath: string };
5
+
6
+ const flatMapRoutes = (route: RouteObject, parentPath: string = ""): RouteWithFullPath[] => {
7
+ // Construct the full path for this route
8
+ let fullPath: string;
9
+
10
+ if (route.index) {
11
+ // Index routes use the parent path
12
+ fullPath = parentPath || "/";
13
+ } else if (route.path) {
14
+ // Combine parent path with current path
15
+ if (route.path.startsWith("/")) {
16
+ fullPath = route.path;
17
+ } else {
18
+ fullPath = parentPath === "/" ? `/${route.path}` : `${parentPath}/${route.path}`;
19
+ }
20
+ } else {
21
+ fullPath = parentPath;
22
+ }
23
+
24
+ const routeWithPath = { ...route, fullPath };
25
+
26
+ // Recursively process children
27
+ const childRoutes = route.children?.flatMap((child) => flatMapRoutes(child, fullPath)) || [];
28
+
29
+ return [routeWithPath, ...childRoutes];
30
+ };
31
+
32
+ export const getAllRoutes = (): RouteWithFullPath[] => {
33
+ return routes.flatMap((route) => flatMapRoutes(route));
34
+ };
@@ -1,8 +1,9 @@
1
1
  import type { RouteObject } from 'react-router';
2
2
  import AppLayout from './appLayout';
3
3
  import Home from '@/pages/Home';
4
- import About from './pages/About';
4
+ import About from './pages/about';
5
5
  import NotFound from './pages/NotFound';
6
+ import New from "./pages/new";
6
7
  import ChartPage from "./pages/ChartPage";
7
8
 
8
9
  export const routes: RouteObject[] = [
@@ -16,14 +17,24 @@ export const routes: RouteObject[] = [
16
17
  handle: { showInNavigation: true, label: 'Home' }
17
18
  },
18
19
  {
19
- path: 'about',
20
+ path: "about",
20
21
  element: <About />,
21
- handle: { showInNavigation: true, label: 'About' }
22
+ handle: { showInNavigation: true, label: "About" }
22
23
  },
23
24
  {
24
25
  path: '*',
25
26
  element: <NotFound />
26
27
  },
28
+ {
29
+ path: "contact",
30
+ element: <h1>Hello World from Contact Page</h1>,
31
+ handle: { showInNavigation: false }
32
+ },
33
+ {
34
+ path: "new",
35
+ element: <New />,
36
+ handle: { showInNavigation: true, label: "New Page" }
37
+ },
27
38
  {
28
39
  path: "chart",
29
40
  element: <ChartPage />,
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-base-sfdx-project-experimental",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "Base SFDX project template",
5
5
  "private": true,
6
6
  "files": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/webapp-template-feature-react-chart-experimental",
3
- "version": "1.26.0",
3
+ "version": "1.27.0",
4
4
  "description": "Chart feature with analytics chart components, agent skills, and rules (Recharts)",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",
@@ -16,7 +16,9 @@
16
16
  },
17
17
  "files": [
18
18
  "dist",
19
- "src"
19
+ "src",
20
+ "rules",
21
+ "skills"
20
22
  ],
21
23
  "publishConfig": {
22
24
  "access": "public"
@@ -28,7 +30,7 @@
28
30
  "watch": "npx tsx ../../cli/src/index.ts watch-patches packages/template/feature/feature-react-chart packages/template/base-app/base-react-app packages/template/feature/feature-react-chart/dist"
29
31
  },
30
32
  "devDependencies": {
31
- "@salesforce/webapp-experimental": "^1.26.0",
33
+ "@salesforce/webapp-experimental": "^1.27.0",
32
34
  "@types/react": "^19.2.7",
33
35
  "@types/react-dom": "^19.2.3",
34
36
  "react-dom": "^19.2.1",
@@ -36,5 +38,5 @@
36
38
  "recharts": "^2.15.0",
37
39
  "vite": "^7.3.1"
38
40
  },
39
- "gitHead": "71a22c47238f6a1ce9696fa5c9247b573351d286"
41
+ "gitHead": "b162807f5062f6aee52605127b787de508035fc9"
40
42
  }
@@ -0,0 +1,27 @@
1
+ ---
2
+ paths:
3
+ - "**/*.tsx"
4
+ - "**/components/**/*.ts"
5
+ ---
6
+
7
+ # Analytics charts (standards)
8
+
9
+ When adding or editing chart UI in this project, follow these conventions.
10
+
11
+ ## Components and library
12
+
13
+ - Use the shared **AnalyticsChart** component (and **ChartContainer** when a framed block is needed). Do not use raw Recharts components for standard line or bar charts.
14
+ - The project must have **recharts** installed (`npm install recharts`).
15
+
16
+ ## Data shape
17
+
18
+ - **Time-series** (line): data must be an array of `{ x: string, y: number }`. Map raw fields (e.g. `date`, `value`) to these keys before passing to the chart.
19
+ - **Categorical** (bar): data must be an array of `{ name: string, value: number }`. Map raw fields (e.g. `category`, `total`) accordingly.
20
+
21
+ ## Theming
22
+
23
+ - Use only the **theme** prop on AnalyticsChart: `red` (decline/loss), `green` (growth/gain), `neutral` (default or mixed). Do not introduce ad-hoc color schemes for these semantics.
24
+
25
+ ## Placement
26
+
27
+ - Render charts inside the existing application frame (e.g. main content or a route). Do not replace the full app shell with a single chart.
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: analytics-charts
3
+ description: Add or change charts from raw data. Use when the user asks for a chart, graph, or analytics visualization from JSON/data.
4
+ ---
5
+
6
+ # Analytics charts (workflow)
7
+
8
+ When the user wants a chart or visualization from data, follow this workflow. Charts use **Recharts** via the **AnalyticsChart** component.
9
+
10
+ ## Dependencies
11
+
12
+ Ensure the following package is installed in the project:
13
+
14
+ ```bash
15
+ npm install recharts
16
+ ```
17
+
18
+ ## 1. Interpret data type
19
+
20
+ - **Time-series**: data over time or ordered (dates, timestamps). Use a line chart. Raw shape often has date-like keys and a numeric value.
21
+ - **Categorical**: data by category (labels, segments). Use a bar chart. Raw shape often has a category name and a numeric value.
22
+
23
+ If the user says "over time", "trend", or uses date-like keys → time-series. If they say "by category", "by X", or use label-like keys → categorical.
24
+
25
+ ## 2. Map data to chart shape
26
+
27
+ - **Time-series**: produce `[{ x: string, y: number }, ...]` (e.g. map `date`→`x`, `value`→`y`).
28
+ - **Categorical**: produce `[{ name: string, value: number }, ...]` (e.g. map `category`→`name`, `total`→`value`).
29
+
30
+ See [schema-mapping.md](docs/schema-mapping.md) for examples.
31
+
32
+ ## 3. Choose theme
33
+
34
+ - **red**: declining, loss, negative trend.
35
+ - **green**: growth, gain, positive trend.
36
+ - **neutral**: default or mixed.
37
+
38
+ ## 4. Generate and place the chart
39
+
40
+ - Use **AnalyticsChart** with `chartType` (`"time-series"` or `"categorical"`), `data` (mapped array), `theme`, and optional `title`. Wrap in **ChartContainer** if the app uses it for chart blocks.
41
+ - Insert the chart inside the existing app (e.g. main content or a route), not as the entire page.
@@ -0,0 +1,4 @@
1
+ # Schema mapping (Recharts)
2
+
3
+ - **Time-series**: `{ x: string, y: number }` — e.g. map `date`→`x`, `value`→`y`.
4
+ - **Categorical**: `{ name: string, value: number }` — e.g. map `category`→`name`, `total`→`value`.