@abgov/jsonforms-components 2.38.3 → 2.38.5

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/index.esm.js CHANGED
@@ -7090,7 +7090,7 @@ let _$6 = t => t,
7090
7090
  _t21$1,
7091
7091
  _t22$1,
7092
7092
  _t23$1,
7093
- _t24,
7093
+ _t24$1,
7094
7094
  _t25,
7095
7095
  _t26,
7096
7096
  _t27,
@@ -7253,7 +7253,7 @@ const CategoryStatus = styled.td(_t23$1 || (_t23$1 = _$6`
7253
7253
  padding-right: var(--goa-space-xl);
7254
7254
  align-content: center;
7255
7255
  `));
7256
- const Center = styled.div(_t24 || (_t24 = _$6`
7256
+ const Center = styled.div(_t24$1 || (_t24$1 = _$6`
7257
7257
  padding-top: 5px;
7258
7258
  `));
7259
7259
  const PageStepperRow = styled.tr(_t25 || (_t25 = _$6`
@@ -9931,7 +9931,8 @@ let _$3 = t => t,
9931
9931
  _t20,
9932
9932
  _t21,
9933
9933
  _t22,
9934
- _t23;
9934
+ _t23,
9935
+ _t24;
9935
9936
  const DeleteDialogContent = styled.div(_t$3 || (_t$3 = _$3`
9936
9937
  margin-bottom: var(--goa-space-m);
9937
9938
  `));
@@ -9986,7 +9987,7 @@ const FlexForm = styled.div(_t10 || (_t10 = _$3`
9986
9987
  flex-direction: column;
9987
9988
  flex: 3;
9988
9989
  `));
9989
- const TabData = styled.div(_t11 || (_t11 = _$3`
9990
+ const TabName = styled.div(_t11 || (_t11 = _$3`
9990
9991
  margin: 1rem 0 1rem 1rem;
9991
9992
  font-weight: 400;
9992
9993
  white-space: nowrap;
@@ -10057,6 +10058,11 @@ const FixTableHeaderAlignment = styled.div(_t23 || (_t23 = _$3`
10057
10058
  text-align: center;
10058
10059
  }
10059
10060
  `));
10061
+ const ListWithDetailsReviewCellDiv = styled.div(_t24 || (_t24 = _$3`
10062
+ display: 'flex';
10063
+ flex-direction: 'column';
10064
+ text-align: 'start';
10065
+ `));
10060
10066
 
10061
10067
  const DeleteDialog = /*#__PURE__*/React.memo(function DeleteDialog({
10062
10068
  open,
@@ -10276,11 +10282,7 @@ const GenerateRows$1 = (Cell, schema, rowPath, enabled, openDeleteDialog, handle
10276
10282
  handleChange,
10277
10283
  errors
10278
10284
  };
10279
- return jsx("div", {
10280
- style: {
10281
- display: 'flex',
10282
- flexDirection: 'column'
10283
- },
10285
+ return jsx(ListWithDetailsReviewCellDiv, {
10284
10286
  children: jsx(Cell, Object.assign({}, props, {
10285
10287
  count: count || 0
10286
10288
  }))
@@ -11076,6 +11078,57 @@ const NonEmptyRowComponent = ({
11076
11078
  }) : null
11077
11079
  }, childPath);
11078
11080
  };
11081
+ const MainItemComponent = ({
11082
+ childPath,
11083
+ rowIndex,
11084
+ openDeleteDialog,
11085
+ selectCurrentTab,
11086
+ enabled,
11087
+ currentTab,
11088
+ current,
11089
+ setCurrentListPage,
11090
+ rowData
11091
+ }) => {
11092
+ const displayName = Object.keys(rowData !== null && rowData !== void 0 ? rowData : {}).length === 0 ? 'No data' : Object.values(rowData || {}).join(', ');
11093
+ return jsx(SideMenuItem, {
11094
+ onClick: () => selectCurrentTab(rowIndex),
11095
+ onKeyDown: e => {
11096
+ if (e.key === 'ArrowRight') {
11097
+ e.preventDefault();
11098
+ if (current) {
11099
+ const goa = current === null || current === void 0 ? void 0 : current.querySelector('goa-input, goa-button');
11100
+ if (goa === null || goa === void 0 ? void 0 : goa.shadowRoot) {
11101
+ const internal = goa.shadowRoot.querySelector('input, button');
11102
+ internal === null || internal === void 0 ? void 0 : internal.focus();
11103
+ selectCurrentTab(rowIndex);
11104
+ }
11105
+ }
11106
+ }
11107
+ },
11108
+ children: jsxs(RowFlexMenu, {
11109
+ tabIndex: 0,
11110
+ children: [jsx(TabName, {
11111
+ children: displayName
11112
+ }), enabled ? jsx(Trash, {
11113
+ children: jsx(GoAIconButton, {
11114
+ disabled: !enabled,
11115
+ icon: "trash",
11116
+ title: 'remove',
11117
+ testId: "remove the details",
11118
+ onClick: () => openDeleteDialog(childPath, rowIndex, displayName)
11119
+ })
11120
+ }) : null, jsx(IconPadding, {
11121
+ children: jsx(GoAIconButton, {
11122
+ disabled: !enabled,
11123
+ icon: "create",
11124
+ title: 'edit',
11125
+ testId: "edit button",
11126
+ onClick: () => setCurrentListPage(currentTab + 1)
11127
+ })
11128
+ })]
11129
+ })
11130
+ });
11131
+ };
11079
11132
  const MainTab = ({
11080
11133
  childPath,
11081
11134
  rowIndex,
@@ -11083,7 +11136,6 @@ const MainTab = ({
11083
11136
  selectCurrentTab,
11084
11137
  enabled,
11085
11138
  currentTab,
11086
- name,
11087
11139
  current,
11088
11140
  setCurrentListPage
11089
11141
  }) => {
@@ -11097,48 +11149,35 @@ const MainTab = ({
11097
11149
  const rowErrors = (_a = core === null || core === void 0 ? void 0 : core.errors) === null || _a === void 0 ? void 0 : _a.filter(e => {
11098
11150
  const base = `/${childPath.replace(/\./g, '/')}`;
11099
11151
  return e.instancePath === base || e.instancePath.startsWith(base + '/');
11100
- }).map(error => error === null || error === void 0 ? void 0 : error.message);
11152
+ }).map(e => {
11153
+ var _a, _b;
11154
+ return ((_b = (_a = e === null || e === void 0 ? void 0 : e.message) === null || _a === void 0 ? void 0 : _a.trim) === null || _b === void 0 ? void 0 : _b.call(_a)) || '';
11155
+ }).filter(msg => msg.length > 0).map((msg, index, arr) => `${msg}${index < arr.length - 1 ? ', ' : ''}`);
11101
11156
  return jsx("div", {
11102
- children: jsx(GoAFormItem, {
11103
- error: (rowErrors === null || rowErrors === void 0 ? void 0 : rowErrors.length) ? rowErrors : undefined,
11104
- children: jsx(SideMenuItem, {
11105
- onClick: () => selectCurrentTab(rowIndex),
11106
- onKeyDown: e => {
11107
- if (e.key === 'ArrowRight') {
11108
- e.preventDefault();
11109
- if (current) {
11110
- const goa = current === null || current === void 0 ? void 0 : current.querySelector('goa-input, goa-button');
11111
- if (goa === null || goa === void 0 ? void 0 : goa.shadowRoot) {
11112
- const internal = goa.shadowRoot.querySelector('input, button');
11113
- internal === null || internal === void 0 ? void 0 : internal.focus();
11114
- selectCurrentTab(rowIndex);
11115
- }
11116
- }
11117
- }
11118
- },
11119
- children: jsxs(RowFlexMenu, {
11120
- tabIndex: 0,
11121
- children: [jsxs(TabData, {
11122
- children: [Object.keys(rowData !== null && rowData !== void 0 ? rowData : {}).length === 0 && 'No data', " ", Object.values(rowData).join(', ')]
11123
- }), enabled ? jsx(Trash, {
11124
- children: jsx(GoAIconButton, {
11125
- disabled: !enabled,
11126
- icon: "trash",
11127
- title: 'remove',
11128
- testId: "remove the details",
11129
- onClick: () => openDeleteDialog(childPath, rowIndex, name)
11130
- })
11131
- }) : null, jsx(IconPadding, {
11132
- children: jsx(GoAIconButton, {
11133
- disabled: !enabled,
11134
- icon: "create",
11135
- title: 'edit',
11136
- testId: "edit button",
11137
- onClick: () => setCurrentListPage(currentTab + 1)
11138
- })
11139
- })]
11140
- })
11157
+ "data-testid": `object-array-main-item-${rowIndex}`,
11158
+ children: (rowErrors === null || rowErrors === void 0 ? void 0 : rowErrors.length) ? jsx(GoAFormItem, {
11159
+ error: (rowErrors === null || rowErrors === void 0 ? void 0 : rowErrors.length) ? rowErrors : null,
11160
+ children: jsx(MainItemComponent, {
11161
+ rowData: rowData,
11162
+ childPath: childPath,
11163
+ rowIndex: rowIndex,
11164
+ openDeleteDialog: openDeleteDialog,
11165
+ selectCurrentTab: selectCurrentTab,
11166
+ enabled: enabled,
11167
+ currentTab: currentTab,
11168
+ current: current,
11169
+ setCurrentListPage: setCurrentListPage
11141
11170
  })
11171
+ }) : jsx(MainItemComponent, {
11172
+ rowData: rowData,
11173
+ childPath: childPath,
11174
+ rowIndex: rowIndex,
11175
+ openDeleteDialog: openDeleteDialog,
11176
+ selectCurrentTab: selectCurrentTab,
11177
+ enabled: enabled,
11178
+ currentTab: currentTab,
11179
+ current: current,
11180
+ setCurrentListPage: setCurrentListPage
11142
11181
  })
11143
11182
  }, childPath);
11144
11183
  };
@@ -11192,10 +11231,6 @@ const ObjectArrayList = ({
11192
11231
  const selectCurrentTab = index => {
11193
11232
  setCurrentIndex(index);
11194
11233
  };
11195
- (() => {
11196
- const str = (appliedUiSchemaOptions === null || appliedUiSchemaOptions === void 0 ? void 0 : appliedUiSchemaOptions.itemLabel) ? `${appliedUiSchemaOptions.itemLabel}` : `${path}`;
11197
- return str.charAt(0).toUpperCase() + str.slice(1);
11198
- })();
11199
11234
  return jsx(ListContainer, {
11200
11235
  children: jsxs(RowFlex, {
11201
11236
  children: [currentListPage === 0 && jsx(FlexTabs, {
@@ -11204,12 +11239,10 @@ const ObjectArrayList = ({
11204
11239
  },
11205
11240
  children: range(data).map(index => {
11206
11241
  const childPath = Paths.compose(path, `${index}`);
11207
- const name = (appliedUiSchemaOptions === null || appliedUiSchemaOptions === void 0 ? void 0 : appliedUiSchemaOptions.itemLabel) ? `${pluralize.singular(appliedUiSchemaOptions === null || appliedUiSchemaOptions === void 0 ? void 0 : appliedUiSchemaOptions.itemLabel)} ${index + 1}` : `${pluralize.singular(path)} ${index + 1}`;
11208
11242
  return jsx(MainTab, {
11209
11243
  childPath: childPath,
11210
11244
  rowIndex: index,
11211
11245
  currentTab: currentIndex,
11212
- name: name,
11213
11246
  openDeleteDialog: openDeleteDialog,
11214
11247
  selectCurrentTab: selectCurrentTab,
11215
11248
  enabled: enabled,
@@ -13135,32 +13168,42 @@ const PhoneNumberWithTypeReviewControl = props => {
13135
13168
  };
13136
13169
  const GoAInputBasePhoneNumberWithTypeReviewControl = withJsonFormsAllOfProps(PhoneNumberWithTypeReviewControl);
13137
13170
 
13171
+ /**
13172
+ * Escape for use in RegExp
13173
+ */
13138
13174
  function escapeRegExp(s) {
13139
13175
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13140
13176
  }
13177
+ /**
13178
+ * Resolve a JSON Pointer–style scope like "#/properties/x" or "#/properties/arr/c3"
13179
+ */
13141
13180
  function resolveScope(scope, data) {
13142
13181
  if (!scope || typeof scope !== 'string') return undefined;
13182
+ // normalize: strip "#/" and optional "properties/"
13143
13183
  const cleaned = scope.replace(/^#\/(properties\/)?/, '');
13144
13184
  const parts = cleaned.split('/').filter(Boolean);
13145
13185
  let cur = data;
13146
- for (const part of parts) {
13147
- if (cur == null) return undefined;
13148
- if (Array.isArray(cur) && /^\d+$/.test(part)) {
13149
- cur = cur[Number(part)];
13150
- } else if (typeof cur === 'object') {
13151
- if (part === 'properties') continue;
13152
- cur = cur[part];
13153
- } else {
13154
- return undefined;
13155
- }
13186
+ for (const p of parts) {
13187
+ if (cur == null || typeof cur !== 'object') return undefined;
13188
+ cur = cur[p];
13156
13189
  }
13157
13190
  return cur;
13158
13191
  }
13159
- function evaluateSum(scope, data) {
13192
+ /**
13193
+ * SUM(#/properties/arr/c3) helper
13194
+ */
13195
+ function evaluateSum(scope, data, opts) {
13160
13196
  if (!scope || typeof scope !== 'string') {
13161
13197
  return {
13162
13198
  value: undefined,
13163
- error: 'SUM() requires a JSON pointer argument.'
13199
+ error: 'SUM requires a scope argument.'
13200
+ };
13201
+ }
13202
+ const normalized = scope.replace(/^#\/(properties\/)?/, '#/properties/');
13203
+ if ((opts === null || opts === void 0 ? void 0 : opts.knownScopes) && !opts.knownScopes.includes(normalized)) {
13204
+ return {
13205
+ value: undefined,
13206
+ error: `Invalid scope(s): ${normalized}`
13164
13207
  };
13165
13208
  }
13166
13209
  const cleaned = scope.replace(/^#\/(properties\/)?/, '');
@@ -13168,83 +13211,62 @@ function evaluateSum(scope, data) {
13168
13211
  if (parts.length < 2) {
13169
13212
  return {
13170
13213
  value: undefined,
13171
- error: `SUM() pointer "${scope}" must include an array and field name.`
13214
+ error: 'SUM requires array/column path like #/properties/arr/c3'
13172
13215
  };
13173
13216
  }
13174
- const fieldKey = parts[parts.length - 1];
13217
+ const colKey = parts[parts.length - 1];
13175
13218
  const arrPath = parts.slice(0, -1);
13176
13219
  let cur = data;
13177
- for (const segment of arrPath) {
13178
- if (cur == null) {
13179
- cur = undefined;
13180
- break;
13181
- }
13182
- if (Array.isArray(cur) && /^\d+$/.test(segment)) {
13183
- cur = cur[Number(segment)];
13184
- } else if (typeof cur === 'object') {
13185
- if (segment === 'properties') continue;
13186
- cur = cur[segment];
13187
- } else {
13220
+ for (const p of arrPath) {
13221
+ if (cur == null || typeof cur !== 'object') {
13188
13222
  cur = undefined;
13189
13223
  break;
13190
13224
  }
13225
+ cur = cur[p];
13191
13226
  }
13192
- if (cur == null) {
13227
+ if (cur === undefined) {
13193
13228
  return {
13194
13229
  value: undefined,
13195
- error: `Please provide values for: ${scope}`
13230
+ error: undefined
13196
13231
  };
13197
13232
  }
13198
13233
  if (!Array.isArray(cur)) {
13199
13234
  return {
13200
13235
  value: undefined,
13201
- error: `SUM() pointer "${scope}" does not resolve to an array.`
13236
+ error: `Expected an array at "#/properties/${arrPath.join('/')}"`
13202
13237
  };
13203
13238
  }
13204
- const missingIndices = [];
13205
- const invalidIndices = [];
13206
13239
  let sum = 0;
13207
- for (let i = 0; i < cur.length; i++) {
13208
- const row = cur[i];
13209
- if (row == null || typeof row !== 'object') {
13210
- missingIndices.push(i);
13211
- continue;
13212
- }
13213
- const v = row[fieldKey];
13214
- if (v === undefined || v === null || v === '') {
13215
- missingIndices.push(i);
13216
- } else if (typeof v !== 'number' || Number.isNaN(v)) {
13217
- invalidIndices.push(i);
13218
- } else {
13240
+ let anyValue = false;
13241
+ let anyMissing = false;
13242
+ for (const row of cur) {
13243
+ if (row == null || typeof row !== 'object') continue;
13244
+ const v = row[colKey];
13245
+ if (typeof v === 'number' && Number.isFinite(v)) {
13246
+ anyValue = true;
13219
13247
  sum += v;
13248
+ } else if (v !== undefined && v !== null) {
13249
+ anyMissing = true;
13220
13250
  }
13221
13251
  }
13222
- if (invalidIndices.length > 0) {
13252
+ if (!anyValue && !anyMissing) {
13223
13253
  return {
13224
13254
  value: undefined,
13225
- error: `Expected numeric values for: ${scope} at rows [${invalidIndices.join(', ')}]`
13255
+ error: undefined
13226
13256
  };
13227
13257
  }
13228
- if (missingIndices.length === cur.length) {
13258
+ if (anyMissing) {
13229
13259
  return {
13230
13260
  value: undefined,
13231
- error: `Please provide values for: ${scope}`
13261
+ error: `Please provide values for: ${normalized}`
13232
13262
  };
13233
13263
  }
13234
13264
  return {
13235
13265
  value: sum,
13236
- error: missingIndices.length ? `Some rows are missing values for: ${scope}` : undefined
13266
+ error: undefined
13237
13267
  };
13238
13268
  }
13239
- /**
13240
- * General expression evaluation.
13241
- * - Supports SUM(#/properties/arr/c3)
13242
- * - Supports arithmetic with JSON pointers:
13243
- * "#/properties/x * #/properties/y + #/properties/z"
13244
- *
13245
- * Returns { value, error } and NEVER throws.
13246
- */
13247
- function evaluateExpression(expression, data) {
13269
+ function evaluateExpression(expression, data, opts) {
13248
13270
  if (!expression || typeof expression !== 'string') {
13249
13271
  return {
13250
13272
  value: undefined,
@@ -13252,66 +13274,63 @@ function evaluateExpression(expression, data) {
13252
13274
  };
13253
13275
  }
13254
13276
  const trimmed = expression.trim();
13255
- if (!trimmed) return {
13256
- value: undefined,
13257
- error: undefined
13258
- };
13277
+ if (!trimmed) {
13278
+ return {
13279
+ value: undefined,
13280
+ error: undefined
13281
+ };
13282
+ }
13259
13283
  const sumMatch = trimmed.match(/^SUM\(\s*['"]?(.+?)['"]?\s*\)$/i);
13260
13284
  if (sumMatch) {
13261
- const pointer = sumMatch[1];
13262
- return evaluateSum(pointer, data);
13285
+ const sumScope = sumMatch[1];
13286
+ return evaluateSum(sumScope, data, opts);
13263
13287
  }
13264
- const scopeRegex = /#\/(?:properties\/)?[^\s"')]+/g;
13288
+ const scopeRegex = /#\/(?:properties\/)?[^\s"'()]+/g;
13265
13289
  const matches = trimmed.match(scopeRegex) || [];
13266
13290
  const uniqueScopes = Array.from(new Set(matches));
13267
- let exprForParse = trimmed;
13268
- const scopeToVar = new Map();
13269
- uniqueScopes.forEach((scope, index) => {
13270
- const varName = `v${index}`;
13271
- scopeToVar.set(scope, varName);
13272
- const pattern = new RegExp(`['"]?${escapeRegExp(scope)}['"]?`, 'g');
13273
- exprForParse = exprForParse.replace(pattern, varName);
13274
- });
13275
- let parsed;
13276
- try {
13277
- const parser = new Parser();
13278
- parsed = parser.parse(exprForParse);
13279
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13280
- } catch (e) {
13281
- return {
13282
- value: undefined,
13283
- error: 'Invalid expression syntax'
13284
- };
13291
+ const normalizedScopes = uniqueScopes.map(s => s.replace(/^#\/(properties\/)?/, '#/properties/'));
13292
+ if ((opts === null || opts === void 0 ? void 0 : opts.knownScopes) && opts.knownScopes.length) {
13293
+ const invalidScopes = normalizedScopes.filter(s => !opts.knownScopes.includes(s));
13294
+ if (invalidScopes.length > 0) {
13295
+ return {
13296
+ value: undefined,
13297
+ error: `Invalid scope(s): ${invalidScopes.join(', ')}`
13298
+ };
13299
+ }
13285
13300
  }
13286
13301
  const vars = {};
13302
+ let expr = trimmed;
13287
13303
  const missingScopes = [];
13288
- const invalidTypeScopes = [];
13289
- for (const scope of uniqueScopes) {
13304
+ let anyValuePresent = false;
13305
+ uniqueScopes.forEach((scope, idx) => {
13306
+ const normalized = scope.replace(/^#\/(properties\/)?/, '#/properties/');
13290
13307
  const val = resolveScope(scope, data);
13291
- if (val === undefined || val === null || val === '') {
13292
- missingScopes.push(scope);
13293
- continue;
13294
- }
13295
- if (typeof val !== 'number' || Number.isNaN(val)) {
13296
- invalidTypeScopes.push(scope);
13297
- continue;
13308
+ if (typeof val === 'number' && Number.isFinite(val)) {
13309
+ anyValuePresent = true;
13310
+ const varName = `v${idx}`;
13311
+ vars[varName] = val;
13312
+ const pattern = new RegExp(`['"]?${escapeRegExp(scope)}['"]?`, 'g');
13313
+ expr = expr.replace(pattern, varName);
13314
+ } else {
13315
+ missingScopes.push(normalized);
13298
13316
  }
13299
- const varName = scopeToVar.get(scope);
13300
- vars[varName] = val;
13301
- }
13302
- if (invalidTypeScopes.length > 0) {
13317
+ });
13318
+ if (!anyValuePresent && missingScopes.length === normalizedScopes.length) {
13303
13319
  return {
13304
13320
  value: undefined,
13305
- error: `Expected numeric values for: ${invalidTypeScopes.join(', ')}`
13321
+ error: undefined
13306
13322
  };
13307
13323
  }
13308
13324
  if (missingScopes.length > 0) {
13325
+ const uniqMissing = Array.from(new Set(missingScopes));
13309
13326
  return {
13310
13327
  value: undefined,
13311
- error: `Please provide values for: ${missingScopes.join(', ')}`
13328
+ error: `Please provide values for: ${uniqMissing.join(', ')}`
13312
13329
  };
13313
13330
  }
13314
13331
  try {
13332
+ const parser = new Parser();
13333
+ const parsed = parser.parse(expr);
13315
13334
  const result = parsed.evaluate(vars);
13316
13335
  if (typeof result === 'number' && Number.isFinite(result)) {
13317
13336
  return {
@@ -13321,15 +13340,30 @@ function evaluateExpression(expression, data) {
13321
13340
  }
13322
13341
  return {
13323
13342
  value: undefined,
13324
- error: 'Expression did not produce a numeric result'
13343
+ error: 'Invalid expression result (not a finite number).'
13325
13344
  };
13326
13345
  } catch (_a) {
13327
13346
  return {
13328
13347
  value: undefined,
13329
- error: 'Error while evaluating expression'
13348
+ error: 'Invalid expression syntax'
13330
13349
  };
13331
13350
  }
13332
13351
  }
13352
+ function collectScopes(schema, base = '#') {
13353
+ if (!schema || typeof schema !== 'object') return [];
13354
+ const scopes = [];
13355
+ if (schema.type === 'object' && schema.properties && typeof schema.properties === 'object') {
13356
+ Object.entries(schema.properties).forEach(([key, subschema]) => {
13357
+ const here = base === '#' ? `#/properties/${key}` : `${base}/${key}`;
13358
+ scopes.push(here);
13359
+ scopes.push(...collectScopes(subschema, here));
13360
+ });
13361
+ }
13362
+ if (schema.type === 'array' && schema.items) {
13363
+ scopes.push(...collectScopes(schema.items, base));
13364
+ }
13365
+ return Array.from(new Set(scopes));
13366
+ }
13333
13367
 
13334
13368
  const GoACalculation = props => {
13335
13369
  var _a;
@@ -13341,33 +13375,36 @@ const GoACalculation = props => {
13341
13375
  visible,
13342
13376
  handleChange
13343
13377
  } = props;
13344
- const label = typeof (uischema === null || uischema === void 0 ? void 0 : uischema.label) === 'string' ? uischema.label : undefined;
13345
- const expression = schema === null || schema === void 0 ? void 0 : schema.description;
13346
13378
  const {
13347
13379
  core
13348
13380
  } = useJsonForms();
13381
+ const rootSchema = core === null || core === void 0 ? void 0 : core.schema;
13349
13382
  const rootData = (_a = core === null || core === void 0 ? void 0 : core.data) !== null && _a !== void 0 ? _a : {};
13383
+ const label = typeof (uischema === null || uischema === void 0 ? void 0 : uischema.label) === 'string' ? uischema.label : undefined;
13384
+ const expression = schema === null || schema === void 0 ? void 0 : schema.description;
13385
+ const knownScopes = useMemo(() => collectScopes(rootSchema), [rootSchema]);
13350
13386
  const [hasInteracted, setHasInteracted] = useState(false);
13351
- const prevDataRef = useRef(rootData);
13352
- useEffect(() => {
13353
- setHasInteracted(was => was ? true : true);
13354
- }, [rootData]);
13387
+ const initialSnapshot = useRef(JSON.stringify(rootData));
13355
13388
  useEffect(() => {
13356
- if (prevDataRef.current !== rootData) {
13389
+ if (hasInteracted) return;
13390
+ const now = JSON.stringify(rootData);
13391
+ if (now !== initialSnapshot.current) {
13357
13392
  setHasInteracted(true);
13358
- prevDataRef.current = rootData;
13359
13393
  }
13360
- }, [rootData]);
13394
+ }, [rootData, hasInteracted]);
13361
13395
  const {
13362
13396
  value: computedValue,
13363
13397
  error
13364
- } = evaluateExpression(expression, rootData);
13398
+ } = evaluateExpression(expression, rootData, {
13399
+ knownScopes
13400
+ });
13365
13401
  useEffect(() => {
13366
13402
  if (computedValue !== undefined && typeof handleChange === 'function' && path) {
13367
13403
  handleChange(path, computedValue);
13368
13404
  }
13369
13405
  }, [computedValue, handleChange, path]);
13370
- const showError = hasInteracted && !!error;
13406
+ const isConfigError = !!error && (error.toLowerCase().includes('invalid scope') || error.toLowerCase().includes('expression syntax'));
13407
+ const showError = !!error && (isConfigError || hasInteracted);
13371
13408
  return jsx(Visible, {
13372
13409
  visible: visible,
13373
13410
  children: jsx(GoAFormItem, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/jsonforms-components",
3
- "version": "2.38.3",
3
+ "version": "2.38.5",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Government of Alberta - React renderers for JSON Forms based on the design system.",
6
6
  "repository": "https://github.com/GovAlta/adsp-monorepo",
@@ -1,15 +1,19 @@
1
- export interface EvaluationResult {
1
+ import { JsonSchema } from '@jsonforms/core';
2
+ export interface EvalResult {
2
3
  value?: number;
3
4
  error?: string;
4
5
  }
6
+ /**
7
+ * Resolve a JSON Pointer–style scope like "#/properties/x" or "#/properties/arr/c3"
8
+ */
5
9
  export declare function resolveScope(scope: string, data: unknown): unknown;
6
- export declare function evaluateSum(scope: string | undefined, data: unknown): EvaluationResult;
7
10
  /**
8
- * General expression evaluation.
9
- * - Supports SUM(#/properties/arr/c3)
10
- * - Supports arithmetic with JSON pointers:
11
- * "#/properties/x * #/properties/y + #/properties/z"
12
- *
13
- * Returns { value, error } and NEVER throws.
11
+ * SUM(#/properties/arr/c3) helper
14
12
  */
15
- export declare function evaluateExpression(expression: string | undefined, data: unknown): EvaluationResult;
13
+ export declare function evaluateSum(scope: string, data: unknown, opts?: {
14
+ knownScopes?: string[];
15
+ }): EvalResult;
16
+ export declare function evaluateExpression(expression: string | undefined, data: unknown, opts?: {
17
+ knownScopes?: string[];
18
+ }): EvalResult;
19
+ export declare function collectScopes(schema: JsonSchema | undefined, base?: string): string[];
@@ -10,7 +10,7 @@ export declare const RowFlex: import("styled-components/dist/types").IStyledComp
10
10
  export declare const RowFlexMenu: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
11
11
  export declare const FlexTabs: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
12
12
  export declare const FlexForm: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
13
- export declare const TabData: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
13
+ export declare const TabName: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
14
14
  export declare const Trash: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
15
15
  export declare const ListContainer: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
16
16
  export declare const IconPadding: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
@@ -23,3 +23,4 @@ export declare const ObjectArrayRequiredTextLabel: import("styled-components/dis
23
23
  export declare const HasErrorLabel: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
24
24
  export declare const HilightCellWarning: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
25
25
  export declare const FixTableHeaderAlignment: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
26
+ export declare const ListWithDetailsReviewCellDiv: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;