TAGLINE
Reference example · built with AI. This control was written with AI (Claude) and tested on a live Dataverse form; its code has not been reviewed line by line. It is published as a worked example and is not maintained — read the source and
SPEC.md(what was measured on the form) before you use it. Fixes are not guaranteed.
Documentation lives on PCFHub, built
from the docs/ directory in this repository. Edit the Markdown here; the hub
recompiles it.
WHAT_IT_DOES
PROPERTIES
ON_THE_HUB
Download the managed solution from the latest release, or from the component's page on the hub, and import it into your environment.
npm install
npm start # the PCF test harness
npm run build
npm run lint
npm run check # what CI runs first: placeholders, pcfhub.json, control shape
npm run smoke # assertions against the built bundle — see dev/
npm run harness # serves dev/harness.html and opens it
npm run shots # retakes media/ from the harness, in headless Chrome
npm run demo-check # every demo preset in the hub's own harness, measurednpm start renders the control; dev/ is for the states it cannot reach. Build
first, then npm run smoke for the assertions, or npm run harness for the
switches — field-level security, a failed business rule, a host that publishes
no theme or no column metadata, and for a dataset control, more than one page.
Both read the bundle npm run build wrote, and both are described in the header
of dev/smoke.js.
npm run harness serves the repository over http:// rather than leaving you to
open the file: over file:// a dataset fixture cannot be fetched and a module
script is refused, and both arrive as an empty control with a CORS error. It
takes --port and --no-open, and needs no dependency — dev/serve.js is
node:http. A React (virtual) control gets one too: dev/fluent-stub.js stands
in for the Fluent the platform would supply, and its header says exactly where
the stand-in is less capable than the real thing.
npm run shots and npm run demo-check drive Chrome or Edge headless over
the DevTools protocol (dev/cdp.js, no dependency) against the page npm run harness serves. shots takes the recipes in dev/shots.js — the control's
own — and says when a retake changed a picture under a name the hub has
already mirrored. demo-check stands the hub's demo harness up locally from
the manifest (dev/hub-demo.html; npm run dev:demo-harness in the hub
repository serves the harness), picks every preset in pcfhub.json, and
fails on a frame that overflows, a harness error, or anything the control
threw; --live runs the same presets on the published page.
Run npm run refreshTypes after every manifest edit — until you do,
context.parameters is typed from the old manifest and tsc will accept code that
cannot work.
To pack the solution locally you need msbuild — either Visual Studio or the Visual Studio Build Tools:
cd Solution
msbuild /t:build /restore /p:configuration=ReleaseBoth zips land in Solution/bin/Release. This is the only local step that compiles
in production mode, so a green npm run build is not evidence the shipping
bundle compiles — and the pack is incremental, so delete obj/, out/,
Solution/obj/ and Solution/bin/ first if you intend to quote a bundle size from
it.
npm run bump -- --minor # every version location, in one edit
npm run release -- --draft # .release-notes.md, from the commits since the last tag
# …rewrite the notes, commit the bump…
npm run release -- --pushOn PowerShell, call the scripts directly — node scripts/version.mjs --minor.
npm swallows a -- flag there, warns "Unknown cli config", and runs the
script with no arguments: it prints the report, changes nothing, and reads as a
bump that found nothing to do.
npm run bump with no argument is a read: it prints every place the version
lives and exits 1 if they disagree. Worth running before anything else, because
the same check otherwise happens in CI — on a Windows runner, after the pack, on
a tag that has already been pushed. npm run check now runs it too.
The version lives in three places, more in a repository holding several controls, and they are checked against each other:
__CONTROL__/ControlManifest.Input.xml→<control version="…">Solution/src/Other/Solution.xml→<Version>package.json→"version"
Doing it by hand is still fine, and then the thing to get right is the tag:
git tag -a --cleanup=verbatim v1.2.3 -F notes.md && git push origin v1.2.3Without --cleanup=verbatim, git drops every ## Heading in the notes as a
comment, silently. npm run release passes it, and then reads the tag back to
confirm the headings survived — because the failure is invisible in the command
that caused it.
The tag message is the release body, and the release body is the changelog on the hub. A lightweight tag gets GitHub's generated notes instead, which for a repository without pull requests is a single compare link — and the workflow warns when that is about to happen.
There is deliberately no CHANGELOG.md. The hub builds the changelog from
release notes, and docs/changelog.md is a hard failure in npm run check.
The release workflow builds, packs both solution types, and attaches them to a GitHub Release. PCFHub picks the release up from its webhook within seconds, or from the hourly sweep otherwise. A sync imports a draft; a person publishes it.
| Path | What it is |
|---|---|
__CONTROL__/ |
The control: manifest, entry point, CSS, localised strings |
Solution/ |
The Dataverse solution that packages it |
dev/ |
A stand-in host: npm run smoke asserts, harness.html shows, shots.js photographs, hub-demo.html is the hub's demo |
SPEC.md |
What building this corrected, and what is verified versus read |
docs/ |
The pages PCFHub publishes — see the comments in each file |
media/ |
Images and video referenced from the docs |
pcfhub.json |
The hub's manifest: identity, links, docs path, demo |
scripts/ |
Template setup and the CI guard that keeps it adopted |