@khester/create-dynamics-app 3.2.0 → 3.4.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/artifacts/registry.d.ts.map +1 -1
- package/dist/artifacts/registry.js +4 -3
- package/dist/artifacts/registry.js.map +1 -1
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/injectDevTools.d.ts +2 -0
- package/dist/injectDevTools.d.ts.map +1 -1
- package/dist/injectDevTools.js +27 -1
- package/dist/injectDevTools.js.map +1 -1
- package/dist/utils/installDependencies.d.ts +15 -0
- package/dist/utils/installDependencies.d.ts.map +1 -1
- package/dist/utils/installDependencies.js +32 -2
- package/dist/utils/installDependencies.js.map +1 -1
- package/package.json +2 -2
- package/templates/_shared/dev-tools/auth/get-token.cjs +1 -1
- package/templates/_shared/dev-tools/metadata-sync/index.js +23 -11
- package/templates/_shared/dev-tools/smoke/test-retrieve.js +45 -16
- package/templates/grid-customizer/README.md +21 -2
- package/templates/grid-customizer/dev/harness/MockGrid.tsx +133 -6
- package/templates/grid-customizer/dev/main.tsx +54 -8
- package/templates/grid-customizer/{{componentName}}/customizers/CellEditorOverrides.tsx +97 -28
- package/templates/pcf-field/dev/harness/dataverse.ts +106 -0
- package/templates/pcf-field/dev/main.tsx +62 -17
- package/templates/static-web-app/api/local.settings.json +7 -0
- package/templates/static-web-app/frontend/vite.config.ts +9 -1
- package/templates/static-web-app/package.json +3 -1
- package/templates/static-web-app/staticwebapp.config.json +4 -1
|
@@ -10,7 +10,13 @@ import {
|
|
|
10
10
|
type HarnessField,
|
|
11
11
|
} from '@dataverse-kit/pcf-dev-harness/shell';
|
|
12
12
|
import { {{componentName}}Component } from '../{{componentName}}/{{componentName}}Component';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
readField,
|
|
15
|
+
saveField,
|
|
16
|
+
readChoiceOptions,
|
|
17
|
+
type ChoiceOption,
|
|
18
|
+
type HttpError,
|
|
19
|
+
} from './harness/dataverse';
|
|
14
20
|
|
|
15
21
|
/**
|
|
16
22
|
* Dev harness — renders the control against either a local value or a REAL Dataverse column.
|
|
@@ -38,20 +44,38 @@ const LIVE_ENABLED = !!import.meta.env.VITE_DYNAMICS_URL;
|
|
|
38
44
|
* column's choice metadata — note the UPPERCASE `Options`/`Value`/`Label`, which is the live
|
|
39
45
|
* PCF API shape, not the form-builder's design-time config. Harmless for the other variants,
|
|
40
46
|
* which ignore `context` entirely.
|
|
47
|
+
*
|
|
48
|
+
* These placeholder options are for MOCK mode only. In Live mode the harness replaces them with
|
|
49
|
+
* the column's REAL choices — see `buildContext` / `readChoiceOptions`.
|
|
41
50
|
*/
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
const MOCK_OPTIONS: ChoiceOption[] = [
|
|
52
|
+
{ Value: 1, Label: 'Option A' },
|
|
53
|
+
{ Value: 2, Label: 'Option B' },
|
|
54
|
+
{ Value: 3, Label: 'Option C' },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Wrap a set of choices in the PCF context shape the control reads.
|
|
59
|
+
*
|
|
60
|
+
* ★ Live mode MUST NOT use the placeholder options — not even as a fallback when the metadata
|
|
61
|
+
* lookup returns nothing. Two distinct failures, and the second is the worse one:
|
|
62
|
+
*
|
|
63
|
+
* - placeholders in live mode with a NON-matching value (the original defect): a real value like
|
|
64
|
+
* 100000002 matches none of 1/2/3, the Dropdown shows its placeholder, and the control looks
|
|
65
|
+
* broken while the read was correct. Recorded against row 02's G5.
|
|
66
|
+
* - placeholders in live mode with a MATCHING value (found in review of #306): the shipped
|
|
67
|
+
* default binding is `accounts` / `accountcategorycode`, whose real values are
|
|
68
|
+
* 1 = "Preferred Customer" and 2 = "Standard". Falling back to the placeholders renders
|
|
69
|
+
* "Option A" — plausible, confidently wrong, and indistinguishable from working. A blank
|
|
70
|
+
* reads as broken; a wrong label reads as fine. Strictly worse than the bug being fixed.
|
|
71
|
+
*
|
|
72
|
+
* So live mode passes `[]` when metadata is unavailable: the optionset variant then degrades to
|
|
73
|
+
* the numeric input it already documents for a control with no choice metadata, which is honest.
|
|
74
|
+
*/
|
|
75
|
+
const buildContext = (options: ChoiceOption[]): ComponentFramework.Context<any> =>
|
|
76
|
+
({
|
|
77
|
+
parameters: { value: { attributes: { Options: options } } },
|
|
78
|
+
}) as unknown as ComponentFramework.Context<any>;
|
|
55
79
|
|
|
56
80
|
/** Turn a failed live call into a red status line, calling out auth failures specifically. */
|
|
57
81
|
function describeError(e: unknown, verb: string): React.ReactNode {
|
|
@@ -71,6 +95,9 @@ const Harness: React.FC = () => {
|
|
|
71
95
|
const [height, setHeight] = usePersistedState('height', 0);
|
|
72
96
|
const [theme, setTheme] = usePersistedState<ThemeChoice>('theme', 'light');
|
|
73
97
|
const [dataSource, setDataSource] = usePersistedState<DataSource>('dataSource', 'mock');
|
|
98
|
+
// Live choice metadata for the bound column. NOT persisted: it belongs to whichever binding
|
|
99
|
+
// was last loaded, and a stale set from a previous column would mislabel the value.
|
|
100
|
+
const [liveOptions, setLiveOptions] = React.useState<ChoiceOption[] | null>(null);
|
|
74
101
|
const [placeholder, setPlaceholder] = usePersistedState('placeholder', 'Enter a value');
|
|
75
102
|
const [disabled, setDisabled] = usePersistedState('disabled', false);
|
|
76
103
|
const [binding, setBinding] = usePersistedState<FieldBinding>('binding', {
|
|
@@ -101,6 +128,9 @@ const Harness: React.FC = () => {
|
|
|
101
128
|
const onDataSourceChange = (next: DataSource): void => {
|
|
102
129
|
setDataSource(next);
|
|
103
130
|
setValue({{valueDefault}});
|
|
131
|
+
// Drop the live choices with the live value. Keeping them would label MOCK mode with the
|
|
132
|
+
// last-loaded column's real options — the mirror image of the bug this fetch fixes.
|
|
133
|
+
setLiveOptions(null);
|
|
104
134
|
setLiveStatus(
|
|
105
135
|
next === 'mock' ? null : <span style={{ color: '#605e5c' }}>Enter a binding and click Load.</span>,
|
|
106
136
|
);
|
|
@@ -114,13 +144,28 @@ const Harness: React.FC = () => {
|
|
|
114
144
|
}
|
|
115
145
|
setLiveStatus(<span style={{ color: '#605e5c' }}>Loading…</span>);
|
|
116
146
|
try {
|
|
117
|
-
|
|
147
|
+
// Fetched together: the VALUE is meaningless to an option-set control without the
|
|
148
|
+
// choices to resolve it against, and issuing them in parallel keeps Load one round trip
|
|
149
|
+
// deep from the user's point of view. `readChoiceOptions` resolves to null for a column
|
|
150
|
+
// that has no choices, which is the normal case for every non-optionset variant.
|
|
151
|
+
const [{ raw, formatted }, options] = await Promise.all([
|
|
152
|
+
readField(binding.entitySetName, binding.recordId, binding.column),
|
|
153
|
+
// `.catch` makes the documented contract true: readChoiceOptions handles non-ok
|
|
154
|
+
// RESPONSES, but a fetch rejection or a JSON parse failure would otherwise propagate
|
|
155
|
+
// through Promise.all and discard a perfectly good value read. (Review of #306.)
|
|
156
|
+
readChoiceOptions(binding.entitySetName, binding.column).catch(() => null),
|
|
157
|
+
]);
|
|
158
|
+
setLiveOptions(options);
|
|
118
159
|
// The control owns its own typing; the harness hands the raw platform value straight
|
|
119
160
|
// through, exactly as the PCF host does via `context.parameters.value.raw`.
|
|
120
161
|
setValue(raw as {{tsType}});
|
|
162
|
+
// ★ A null metadata result gets its own signal. Without one, a choice column whose
|
|
163
|
+
// metadata could not be read looks identical to a successful load — green status, empty
|
|
164
|
+
// dropdown — and the reader has nothing to go on. (Review of #306.)
|
|
121
165
|
setLiveStatus(
|
|
122
|
-
<span style={{ color: '#107c10' }}>
|
|
166
|
+
<span style={{ color: options === null ? '#8a6116' : '#107c10' }}>
|
|
123
167
|
Loaded{formatted ? ` — "${formatted}"` : ''}.
|
|
168
|
+
{options === null ? ' No choice metadata for this column — the value is shown raw.' : ''}
|
|
124
169
|
</span>,
|
|
125
170
|
);
|
|
126
171
|
} catch (e) {
|
|
@@ -193,7 +238,7 @@ const Harness: React.FC = () => {
|
|
|
193
238
|
disabled={disabled}
|
|
194
239
|
onChange={setValue}
|
|
195
240
|
onBlur={() => undefined}
|
|
196
|
-
context={
|
|
241
|
+
context={buildContext(dataSource === 'live' ? (liveOptions ?? []) : MOCK_OPTIONS)}
|
|
197
242
|
/>
|
|
198
243
|
{dataSource === 'live' && (
|
|
199
244
|
<button
|
|
@@ -5,8 +5,16 @@ export default defineConfig({
|
|
|
5
5
|
plugins: [react()],
|
|
6
6
|
build: { outDir: 'dist' },
|
|
7
7
|
server: {
|
|
8
|
+
// ★ PINNED, and `strictPort` is load-bearing. The root `dev` script runs
|
|
9
|
+
// `swa start http://localhost:5173`, so the emulator proxies to a hard-coded URL. Vite's
|
|
10
|
+
// default on a busy port is to move silently to 5174/5175/…, which would leave `swa`
|
|
11
|
+
// proxying to a dead port — the shell would serve and every module would fail. `strictPort`
|
|
12
|
+
// turns that into a loud failure instead.
|
|
13
|
+
port: 5173,
|
|
14
|
+
strictPort: true,
|
|
8
15
|
proxy: {
|
|
9
|
-
//
|
|
16
|
+
// Only used when Vite is hit DIRECTLY on :5173 (`npm --prefix frontend run dev` without
|
|
17
|
+
// the emulator). Under `npm run dev` the SWA emulator owns /api routing.
|
|
10
18
|
'/api': 'http://localhost:7071',
|
|
11
19
|
},
|
|
12
20
|
},
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
"description": "{{projectName}} — Azure Static Web App (React Fluent {{componentLibrary}} + Functions API)",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"install:all": "npm install && npm --prefix frontend install && npm --prefix api install",
|
|
8
|
-
"dev": "swa start
|
|
8
|
+
"dev": "npm --prefix api run build && swa start http://localhost:5173 --api-location api --run \"npm --prefix frontend run dev\"",
|
|
9
|
+
"dev:api": "npm --prefix api run watch",
|
|
9
10
|
"build": "npm --prefix frontend run build && npm --prefix api run build",
|
|
11
|
+
"deploy": "swa deploy ./frontend/dist --api-location ./api --api-language node --api-version 20 --env production",
|
|
10
12
|
"typecheck": "npm --prefix frontend run typecheck && npm --prefix api run typecheck"
|
|
11
13
|
},
|
|
12
14
|
"devDependencies": {
|