@abgov/jsonforms-components 2.36.1 → 2.37.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/index.esm.js CHANGED
@@ -928,7 +928,6 @@ var PROPER = EXISTS && (function something() { /* empty */ }).name === 'somethin
928
928
  var CONFIGURABLE = EXISTS && (!DESCRIPTORS$c || (DESCRIPTORS$c && getDescriptor(FunctionPrototype$1, 'name').configurable));
929
929
 
930
930
  var functionName = {
931
- EXISTS: EXISTS,
932
931
  PROPER: PROPER,
933
932
  CONFIGURABLE: CONFIGURABLE
934
933
  };
@@ -2329,9 +2328,6 @@ var createMethod$2 = function (CONVERT_TO_STRING) {
2329
2328
  };
2330
2329
 
2331
2330
  var stringMultibyte = {
2332
- // `String.prototype.codePointAt` method
2333
- // https://tc39.es/ecma262/#sec-string.prototype.codepointat
2334
- codeAt: createMethod$2(false),
2335
2331
  // `String.prototype.at` method
2336
2332
  // https://github.com/mathiasbynens/String.prototype.at
2337
2333
  charAt: createMethod$2(true)
@@ -4042,9 +4038,7 @@ if (!set || !clear) {
4042
4038
  }
4043
4039
 
4044
4040
  var task$1 = {
4045
- set: set,
4046
- clear: clear
4047
- };
4041
+ set: set};
4048
4042
 
4049
4043
  var global$8 = global$q;
4050
4044
  var DESCRIPTORS$3 = descriptors;
@@ -5217,7 +5211,7 @@ const PageReviewValueCol = styled.td(_t5$5 || (_t5$5 = _$a`
5217
5211
  `));
5218
5212
  //Check and unchecked are different heights otherwise
5219
5213
  const CheckboxWrapper = styled.div(_t6$3 || (_t6$3 = _$a`
5220
- height: 28px;
5214
+ min-height: 28px;
5221
5215
  `));
5222
5216
  styled.h4(_t7$2 || (_t7$2 = _$a`
5223
5217
  margin: 0 0 0.25rem 0;
@@ -5400,12 +5394,6 @@ var createMethod$1 = function (TYPE) {
5400
5394
  };
5401
5395
 
5402
5396
  var stringTrim = {
5403
- // `String.prototype.{ trimLeft, trimStart }` methods
5404
- // https://tc39.es/ecma262/#sec-string.prototype.trimstart
5405
- start: createMethod$1(1),
5406
- // `String.prototype.{ trimRight, trimEnd }` methods
5407
- // https://tc39.es/ecma262/#sec-string.prototype.trimend
5408
- end: createMethod$1(2),
5409
5397
  // `String.prototype.trim` method
5410
5398
  // https://tc39.es/ecma262/#sec-string.prototype.trim
5411
5399
  trim: createMethod$1(3)
@@ -5884,7 +5872,6 @@ const GoAInputText = props => {
5884
5872
  setIsVisited();
5885
5873
  }
5886
5874
  onChangeForInputControl({
5887
- name,
5888
5875
  value: formattedValue,
5889
5876
  controlProps: props
5890
5877
  });
@@ -5894,7 +5881,6 @@ const GoAInputText = props => {
5894
5881
  setIsVisited();
5895
5882
  }
5896
5883
  onBlurForTextControl({
5897
- name,
5898
5884
  controlProps: props,
5899
5885
  value: autoCapitalize ? value.toUpperCase() : value
5900
5886
  });
@@ -5951,14 +5937,12 @@ const MultiLineText = props => {
5951
5937
  }
5952
5938
  if (value.length === 0 || required && errors.length === 0 && value.length > 0) {
5953
5939
  onKeyPressForTextControl({
5954
- name,
5955
5940
  value: newValue,
5956
5941
  key,
5957
5942
  controlProps: props
5958
5943
  });
5959
5944
  }
5960
5945
  onChangeForInputControl({
5961
- name,
5962
5946
  value: newValue,
5963
5947
  controlProps: props
5964
5948
  });
@@ -6079,14 +6063,12 @@ const GoADateInput = props => {
6079
6063
  setIsVisited();
6080
6064
  }
6081
6065
  onChangeForDateControl({
6082
- name,
6083
6066
  value,
6084
6067
  controlProps: props
6085
6068
  });
6086
6069
  },
6087
6070
  onKeyPress: (name, value, key) => {
6088
6071
  onKeyPressForDateControl({
6089
- name,
6090
6072
  value,
6091
6073
  key,
6092
6074
  controlProps: props
@@ -6097,7 +6079,6 @@ const GoADateInput = props => {
6097
6079
  setIsVisited();
6098
6080
  }
6099
6081
  onBlurForDateControl({
6100
- name,
6101
6082
  value,
6102
6083
  controlProps: props
6103
6084
  });
@@ -6152,7 +6133,6 @@ const GoANumberInput = props => {
6152
6133
  setIsVisited();
6153
6134
  }
6154
6135
  onBlurForNumericControl({
6155
- name,
6156
6136
  value,
6157
6137
  controlProps: props
6158
6138
  });
@@ -6162,7 +6142,6 @@ const GoANumberInput = props => {
6162
6142
  setIsVisited();
6163
6143
  }
6164
6144
  onChangeForNumericControl({
6165
- name,
6166
6145
  value,
6167
6146
  controlProps: props
6168
6147
  });
@@ -6217,7 +6196,6 @@ const GoAInputInteger = props => {
6217
6196
  setIsVisited();
6218
6197
  }
6219
6198
  onBlurForNumericControl({
6220
- name,
6221
6199
  value,
6222
6200
  controlProps: props
6223
6201
  });
@@ -6227,7 +6205,6 @@ const GoAInputInteger = props => {
6227
6205
  setIsVisited();
6228
6206
  }
6229
6207
  onChangeForNumericControl({
6230
- name,
6231
6208
  value,
6232
6209
  controlProps: props
6233
6210
  });
@@ -6269,14 +6246,12 @@ const GoADateTimeInput = props => {
6269
6246
  setIsVisited();
6270
6247
  }
6271
6248
  onChangeForDateTimeControl({
6272
- name,
6273
6249
  value,
6274
6250
  controlProps: props
6275
6251
  });
6276
6252
  },
6277
6253
  onKeyPress: (name, value, key) => {
6278
6254
  onKeyPressForDateControl({
6279
- name,
6280
6255
  value,
6281
6256
  key,
6282
6257
  controlProps: props
@@ -6287,7 +6262,6 @@ const GoADateTimeInput = props => {
6287
6262
  setIsVisited();
6288
6263
  }
6289
6264
  onBlurForDateControl({
6290
- name,
6291
6265
  value,
6292
6266
  controlProps: props
6293
6267
  });
@@ -6341,7 +6315,6 @@ const GoATimeInput = props => {
6341
6315
  onChange: (name, value) => {},
6342
6316
  onKeyPress: (name, value, key) => {
6343
6317
  onKeyPressForTimeControl({
6344
- name,
6345
6318
  value,
6346
6319
  key,
6347
6320
  controlProps: props
@@ -6982,7 +6955,6 @@ const GoAEmailInput = props => {
6982
6955
  setIsVisited(true);
6983
6956
  }
6984
6957
  onChangeForInputControl({
6985
- name,
6986
6958
  value,
6987
6959
  controlProps: props
6988
6960
  });
@@ -6992,7 +6964,6 @@ const GoAEmailInput = props => {
6992
6964
  setIsVisited(true);
6993
6965
  }
6994
6966
  onBlurForTextControl({
6995
- name,
6996
6967
  value,
6997
6968
  controlProps: props
6998
6969
  });
@@ -7608,11 +7579,7 @@ var createMethod = function (IS_RIGHT) {
7608
7579
  var arrayReduce = {
7609
7580
  // `Array.prototype.reduce` method
7610
7581
  // https://tc39.es/ecma262/#sec-array.prototype.reduce
7611
- left: createMethod(false),
7612
- // `Array.prototype.reduceRight` method
7613
- // https://tc39.es/ecma262/#sec-array.prototype.reduceright
7614
- right: createMethod(true)
7615
- };
7582
+ left: createMethod(false)};
7616
7583
 
7617
7584
  var fails$2 = fails$v;
7618
7585
 
@@ -10431,7 +10398,6 @@ const NonEmptyCellComponent$1 = /*#__PURE__*/React.memo(function NonEmptyCellCom
10431
10398
  error: error === null || error === void 0 ? void 0 : error.message,
10432
10399
  isRequired: (_a = required === null || required === void 0 ? void 0 : required.includes(tableKeys[element])) !== null && _a !== void 0 ? _a : false,
10433
10400
  errors: errors !== undefined ? errors : [],
10434
- count: count !== undefined ? count : -1,
10435
10401
  element,
10436
10402
  rowPath,
10437
10403
  index: i
@@ -12449,7 +12415,7 @@ const NameInputs = ({
12449
12415
  // eslint-disable-next-line
12450
12416
  const handleRequiredFieldBlur = (name, updatedData) => {
12451
12417
  const err = Object.assign({}, errors);
12452
- if ((!(data === null || data === void 0 ? void 0 : data[name]) || (data === null || data === void 0 ? void 0 : data[name]) === '') && requiredFields.includes(name) && (!updatedData)) {
12418
+ if ((!(data === null || data === void 0 ? void 0 : data[name]) || (data === null || data === void 0 ? void 0 : data[name]) === '') && requiredFields.includes(name) && (true)) {
12453
12419
  const modifiedName = name === 'firstName' ? 'First name' : name === 'lastName' ? 'Last name' : '';
12454
12420
  err[name] = `${modifiedName} is required`;
12455
12421
  } else {
@@ -13056,71 +13022,191 @@ function resolveScope(scope, data) {
13056
13022
  const cleaned = scope.replace(/^#\/(properties\/)?/, '');
13057
13023
  const parts = cleaned.split('/').filter(Boolean);
13058
13024
  let cur = data;
13059
- for (const p of parts) {
13060
- if (cur == null || typeof cur !== 'object') return undefined;
13061
- cur = cur[p];
13025
+ for (const part of parts) {
13026
+ if (cur == null) return undefined;
13027
+ if (Array.isArray(cur) && /^\d+$/.test(part)) {
13028
+ cur = cur[Number(part)];
13029
+ } else if (typeof cur === 'object') {
13030
+ if (part === 'properties') continue;
13031
+ cur = cur[part];
13032
+ } else {
13033
+ return undefined;
13034
+ }
13062
13035
  }
13063
13036
  return cur;
13064
13037
  }
13065
- function sumColumn(scope, data) {
13066
- if (!scope || typeof scope !== 'string') return undefined;
13067
- // normalize scope: remove leading "#/" and optional "properties/"
13038
+ function evaluateSum(scope, data) {
13039
+ if (!scope || typeof scope !== 'string') {
13040
+ return {
13041
+ value: undefined,
13042
+ error: 'SUM() requires a JSON pointer argument.'
13043
+ };
13044
+ }
13068
13045
  const cleaned = scope.replace(/^#\/(properties\/)?/, '');
13069
13046
  const parts = cleaned.split('/').filter(Boolean);
13070
- if (parts.length < 2) return undefined;
13071
- const colKey = parts[parts.length - 1];
13047
+ if (parts.length < 2) {
13048
+ return {
13049
+ value: undefined,
13050
+ error: `SUM() pointer "${scope}" must include an array and field name.`
13051
+ };
13052
+ }
13053
+ const fieldKey = parts[parts.length - 1];
13072
13054
  const arrPath = parts.slice(0, -1);
13073
13055
  let cur = data;
13074
- for (const p of arrPath) {
13075
- if (cur == null) return undefined;
13076
- // handle numeric indices for arrays
13077
- if (Array.isArray(cur) && /^\d+$/.test(p)) {
13078
- cur = cur[Number(p)];
13056
+ for (const segment of arrPath) {
13057
+ if (cur == null) {
13058
+ cur = undefined;
13059
+ break;
13060
+ }
13061
+ if (Array.isArray(cur) && /^\d+$/.test(segment)) {
13062
+ cur = cur[Number(segment)];
13063
+ } else if (typeof cur === 'object') {
13064
+ if (segment === 'properties') continue;
13065
+ cur = cur[segment];
13079
13066
  } else {
13080
- cur = cur[p];
13067
+ cur = undefined;
13068
+ break;
13081
13069
  }
13082
13070
  }
13083
- if (!Array.isArray(cur)) return undefined;
13071
+ if (cur == null) {
13072
+ return {
13073
+ value: undefined,
13074
+ error: `Please provide values for: ${scope}`
13075
+ };
13076
+ }
13077
+ if (!Array.isArray(cur)) {
13078
+ return {
13079
+ value: undefined,
13080
+ error: `SUM() pointer "${scope}" does not resolve to an array.`
13081
+ };
13082
+ }
13083
+ const missingIndices = [];
13084
+ const invalidIndices = [];
13084
13085
  let sum = 0;
13085
- for (const row of cur) {
13086
- if (row == null) return undefined;
13087
- const v = row[colKey];
13088
- if (typeof v !== 'number') return undefined;
13089
- sum += v;
13086
+ for (let i = 0; i < cur.length; i++) {
13087
+ const row = cur[i];
13088
+ if (row == null || typeof row !== 'object') {
13089
+ missingIndices.push(i);
13090
+ continue;
13091
+ }
13092
+ const v = row[fieldKey];
13093
+ if (v === undefined || v === null || v === '') {
13094
+ missingIndices.push(i);
13095
+ } else if (typeof v !== 'number' || Number.isNaN(v)) {
13096
+ invalidIndices.push(i);
13097
+ } else {
13098
+ sum += v;
13099
+ }
13100
+ }
13101
+ if (invalidIndices.length > 0) {
13102
+ return {
13103
+ value: undefined,
13104
+ error: `Expected numeric values for: ${scope} at rows [${invalidIndices.join(', ')}]`
13105
+ };
13090
13106
  }
13091
- return sum;
13107
+ if (missingIndices.length === cur.length) {
13108
+ return {
13109
+ value: undefined,
13110
+ error: `Please provide values for: ${scope}`
13111
+ };
13112
+ }
13113
+ return {
13114
+ value: sum,
13115
+ error: missingIndices.length ? `Some rows are missing values for: ${scope}` : undefined
13116
+ };
13092
13117
  }
13118
+ /**
13119
+ * General expression evaluation.
13120
+ * - Supports SUM(#/properties/arr/c3)
13121
+ * - Supports arithmetic with JSON pointers:
13122
+ * "#/properties/x * #/properties/y + #/properties/z"
13123
+ *
13124
+ * Returns { value, error } and NEVER throws.
13125
+ */
13093
13126
  function evaluateExpression(expression, data) {
13094
- if (!expression || typeof expression !== 'string') return undefined;
13127
+ if (!expression || typeof expression !== 'string') {
13128
+ return {
13129
+ value: undefined,
13130
+ error: undefined
13131
+ };
13132
+ }
13095
13133
  const trimmed = expression.trim();
13134
+ if (!trimmed) return {
13135
+ value: undefined,
13136
+ error: undefined
13137
+ };
13096
13138
  const sumMatch = trimmed.match(/^SUM\(\s*['"]?(.+?)['"]?\s*\)$/i);
13097
13139
  if (sumMatch) {
13098
- return sumColumn(sumMatch[1], data);
13140
+ const pointer = sumMatch[1];
13141
+ return evaluateSum(pointer, data);
13099
13142
  }
13100
- // Find unique scope tokens like #/properties/x or #/arr/0/c3
13101
13143
  const scopeRegex = /#\/(?:properties\/)?[^\s"')]+/g;
13102
13144
  const matches = trimmed.match(scopeRegex) || [];
13103
13145
  const uniqueScopes = Array.from(new Set(matches));
13104
- // Prepare variable mapping for expr-eval
13146
+ let exprForParse = trimmed;
13147
+ const scopeToVar = new Map();
13148
+ uniqueScopes.forEach((scope, index) => {
13149
+ const varName = `v${index}`;
13150
+ scopeToVar.set(scope, varName);
13151
+ const pattern = new RegExp(`['"]?${escapeRegExp(scope)}['"]?`, 'g');
13152
+ exprForParse = exprForParse.replace(pattern, varName);
13153
+ });
13154
+ let parsed;
13155
+ try {
13156
+ const parser = new Parser();
13157
+ parsed = parser.parse(exprForParse);
13158
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13159
+ } catch (e) {
13160
+ return {
13161
+ value: undefined,
13162
+ error: 'Invalid expression syntax'
13163
+ };
13164
+ }
13105
13165
  const vars = {};
13106
- let expr = trimmed;
13107
- for (let i = 0; i < uniqueScopes.length; i++) {
13108
- const scope = uniqueScopes[i];
13166
+ const missingScopes = [];
13167
+ const invalidTypeScopes = [];
13168
+ for (const scope of uniqueScopes) {
13109
13169
  const val = resolveScope(scope, data);
13110
- if (val === undefined || typeof val !== 'number') return undefined;
13111
- const varName = `v${i}`;
13170
+ if (val === undefined || val === null || val === '') {
13171
+ missingScopes.push(scope);
13172
+ continue;
13173
+ }
13174
+ if (typeof val !== 'number' || Number.isNaN(val)) {
13175
+ invalidTypeScopes.push(scope);
13176
+ continue;
13177
+ }
13178
+ const varName = scopeToVar.get(scope);
13112
13179
  vars[varName] = val;
13113
- const pattern = new RegExp(`['"]?${escapeRegExp(scope)}['"]?`, 'g');
13114
- expr = expr.replace(pattern, varName);
13115
13180
  }
13116
- // Evaluate with expr-eval parser
13181
+ if (invalidTypeScopes.length > 0) {
13182
+ return {
13183
+ value: undefined,
13184
+ error: `Expected numeric values for: ${invalidTypeScopes.join(', ')}`
13185
+ };
13186
+ }
13187
+ if (missingScopes.length > 0) {
13188
+ return {
13189
+ value: undefined,
13190
+ error: `Please provide values for: ${missingScopes.join(', ')}`
13191
+ };
13192
+ }
13117
13193
  try {
13118
- const parser = new Parser();
13119
- const parsed = parser.parse(expr);
13120
13194
  const result = parsed.evaluate(vars);
13121
- return typeof result === 'number' && Number.isFinite(result) ? result : undefined;
13195
+ if (typeof result === 'number' && Number.isFinite(result)) {
13196
+ return {
13197
+ value: result,
13198
+ error: undefined
13199
+ };
13200
+ }
13201
+ return {
13202
+ value: undefined,
13203
+ error: 'Expression did not produce a numeric result'
13204
+ };
13122
13205
  } catch (_a) {
13123
- return undefined;
13206
+ return {
13207
+ value: undefined,
13208
+ error: 'Error while evaluating expression'
13209
+ };
13124
13210
  }
13125
13211
  }
13126
13212
 
@@ -13128,29 +13214,44 @@ const GoACalculation = props => {
13128
13214
  var _a;
13129
13215
  const {
13130
13216
  uischema,
13131
- data,
13132
13217
  schema,
13133
13218
  path,
13134
13219
  id,
13135
13220
  visible,
13136
13221
  handleChange
13137
13222
  } = props;
13138
- const expression = schema === null || schema === void 0 ? void 0 : schema.description;
13139
13223
  const label = typeof (uischema === null || uischema === void 0 ? void 0 : uischema.label) === 'string' ? uischema.label : undefined;
13224
+ const expression = schema === null || schema === void 0 ? void 0 : schema.description;
13140
13225
  const {
13141
13226
  core
13142
13227
  } = useJsonForms();
13143
13228
  const rootData = (_a = core === null || core === void 0 ? void 0 : core.data) !== null && _a !== void 0 ? _a : {};
13144
- const computedValue = expression ? evaluateExpression(expression, rootData) : undefined;
13145
- React.useEffect(() => {
13229
+ const [hasInteracted, setHasInteracted] = useState(false);
13230
+ const prevDataRef = useRef(rootData);
13231
+ useEffect(() => {
13232
+ setHasInteracted(was => was ? true : true);
13233
+ }, [rootData]);
13234
+ useEffect(() => {
13235
+ if (prevDataRef.current !== rootData) {
13236
+ setHasInteracted(true);
13237
+ prevDataRef.current = rootData;
13238
+ }
13239
+ }, [rootData]);
13240
+ const {
13241
+ value: computedValue,
13242
+ error
13243
+ } = evaluateExpression(expression, rootData);
13244
+ useEffect(() => {
13146
13245
  if (computedValue !== undefined && typeof handleChange === 'function' && path) {
13147
13246
  handleChange(path, computedValue);
13148
13247
  }
13149
13248
  }, [computedValue, handleChange, path]);
13249
+ const showError = hasInteracted && !!error;
13150
13250
  return jsx(Visible, {
13151
13251
  visible: visible,
13152
13252
  children: jsx(GoAFormItem, {
13153
13253
  label: label,
13254
+ error: showError ? error : '',
13154
13255
  children: jsx(GoAInput, {
13155
13256
  name: `computed-input-${id}`,
13156
13257
  testId: `computed-input-${id}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/jsonforms-components",
3
- "version": "2.36.1",
3
+ "version": "2.37.0",
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,3 +1,15 @@
1
+ export interface EvaluationResult {
2
+ value?: number;
3
+ error?: string;
4
+ }
1
5
  export declare function resolveScope(scope: string, data: unknown): unknown;
2
- export declare function sumColumn(scope: string, data: unknown): number | undefined;
3
- export declare function evaluateExpression(expression: string, data: unknown): number | undefined;
6
+ export declare function evaluateSum(scope: string | undefined, data: unknown): EvaluationResult;
7
+ /**
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.
14
+ */
15
+ export declare function evaluateExpression(expression: string | undefined, data: unknown): EvaluationResult;