fluentui-extended 2026.8.31 → 2026.8.35

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/index.d.mts CHANGED
@@ -140,6 +140,8 @@ interface QueryBuilderCondition {
140
140
  nestedLogic?: 'and' | 'or';
141
141
  /** Fields available for the related entity (loaded dynamically) */
142
142
  nestedFields?: QueryBuilderField[];
143
+ /** True when the attribute from parsed FetchXML didn't match any known field */
144
+ isUnknownField?: boolean;
143
145
  }
144
146
  interface QueryBuilderGroup {
145
147
  id: string;
@@ -216,6 +218,8 @@ interface QueryBuilderProps {
216
218
  onFetchEntityFields?: (entityLogicalName: string) => Promise<QueryBuilderField[]>;
217
219
  /** Debug/trace callback for logging component behavior */
218
220
  onTrace?: (message: string, data?: any) => void;
221
+ /** Enable verbose console.debug tracing (disabled by default) */
222
+ debug?: boolean;
219
223
  }
220
224
 
221
225
  /**
package/dist/index.d.ts CHANGED
@@ -140,6 +140,8 @@ interface QueryBuilderCondition {
140
140
  nestedLogic?: 'and' | 'or';
141
141
  /** Fields available for the related entity (loaded dynamically) */
142
142
  nestedFields?: QueryBuilderField[];
143
+ /** True when the attribute from parsed FetchXML didn't match any known field */
144
+ isUnknownField?: boolean;
143
145
  }
144
146
  interface QueryBuilderGroup {
145
147
  id: string;
@@ -216,6 +218,8 @@ interface QueryBuilderProps {
216
218
  onFetchEntityFields?: (entityLogicalName: string) => Promise<QueryBuilderField[]>;
217
219
  /** Debug/trace callback for logging component behavior */
218
220
  onTrace?: (message: string, data?: any) => void;
221
+ /** Enable verbose console.debug tracing (disabled by default) */
222
+ debug?: boolean;
219
223
  }
220
224
 
221
225
  /**
package/dist/index.js CHANGED
@@ -971,6 +971,72 @@ var useQueryBuilderStyles = reactComponents.makeStyles({
971
971
  backgroundColor: reactComponents.tokens.colorPaletteRedBackground1,
972
972
  borderRadius: reactComponents.tokens.borderRadiusSmall
973
973
  },
974
+ conditionUnknownBanner: {
975
+ display: "flex",
976
+ alignItems: "center",
977
+ gap: reactComponents.tokens.spacingHorizontalXS,
978
+ paddingTop: "2px",
979
+ paddingBottom: "2px",
980
+ paddingLeft: reactComponents.tokens.spacingHorizontalXS,
981
+ paddingRight: reactComponents.tokens.spacingHorizontalXS,
982
+ marginBottom: reactComponents.tokens.spacingVerticalXS,
983
+ backgroundColor: reactComponents.tokens.colorStatusWarningBackground1,
984
+ borderRadius: reactComponents.tokens.borderRadiusSmall,
985
+ fontSize: reactComponents.tokens.fontSizeBase100,
986
+ color: reactComponents.tokens.colorStatusWarningForeground3
987
+ },
988
+ conditionUnknownText: {
989
+ flex: "1 1 auto"
990
+ },
991
+ conditionUnknownButton: {
992
+ fontSize: reactComponents.tokens.fontSizeBase100,
993
+ minWidth: "0",
994
+ paddingTop: "0",
995
+ paddingBottom: "0",
996
+ paddingLeft: reactComponents.tokens.spacingHorizontalXS,
997
+ paddingRight: reactComponents.tokens.spacingHorizontalXS,
998
+ height: "20px",
999
+ color: reactComponents.tokens.colorStatusWarningForeground3
1000
+ },
1001
+ dialogSurfaceNarrow: {
1002
+ maxWidth: "600px"
1003
+ },
1004
+ dialogSurfaceCompact: {
1005
+ maxWidth: "500px"
1006
+ },
1007
+ dialogUploadContent: {
1008
+ display: "flex",
1009
+ flexDirection: "column",
1010
+ gap: reactComponents.tokens.spacingVerticalM
1011
+ },
1012
+ dialogValidationContent: {
1013
+ display: "flex",
1014
+ flexDirection: "column",
1015
+ gap: reactComponents.tokens.spacingVerticalL
1016
+ },
1017
+ monacoTextarea: {
1018
+ minHeight: "200px",
1019
+ fontFamily: reactComponents.tokens.fontFamilyMonospace
1020
+ },
1021
+ uploadErrorText: {
1022
+ color: reactComponents.tokens.colorStatusDangerForeground1
1023
+ },
1024
+ validationSectionTitle: {
1025
+ display: "flex",
1026
+ alignItems: "center",
1027
+ gap: reactComponents.tokens.spacingHorizontalXXS
1028
+ },
1029
+ validationApiRow: {
1030
+ display: "flex",
1031
+ alignItems: "center",
1032
+ gap: reactComponents.tokens.spacingHorizontalS
1033
+ },
1034
+ emptyRelatedEntity: {
1035
+ paddingTop: reactComponents.tokens.spacingVerticalS,
1036
+ paddingBottom: reactComponents.tokens.spacingVerticalS,
1037
+ color: reactComponents.tokens.colorNeutralForeground3,
1038
+ fontSize: reactComponents.tokens.fontSizeBase200
1039
+ },
974
1040
  validationIcon: {
975
1041
  marginRight: reactComponents.tokens.spacingHorizontalXS
976
1042
  },
@@ -2249,7 +2315,8 @@ var createParsedCondition = (attr, operator, value, value2, fieldMatch, valueDis
2249
2315
  value,
2250
2316
  value2: value2 ?? "",
2251
2317
  ...valueDisplayName ? { valueDisplayName } : {},
2252
- ...entityAlias ? { entityAlias } : {}
2318
+ ...entityAlias ? { entityAlias } : {},
2319
+ ...!fieldMatch ? { isUnknownField: true } : {}
2253
2320
  });
2254
2321
  var parseConditionElement = (condEl, fields) => {
2255
2322
  const attr = condEl.getAttribute("attribute") || "";
@@ -2896,14 +2963,16 @@ var enrichOptionsetFields = async (fields, entityName, trace) => {
2896
2963
  var QueryBuilder = (props) => {
2897
2964
  const styles = useQueryBuilderStyles();
2898
2965
  const trace = React3__namespace.useCallback((message, data) => {
2899
- console.debug(
2900
- "%c FluentUI-Extended ",
2901
- "background: #845EF7; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;",
2902
- message,
2903
- data || ""
2904
- );
2966
+ if (props.debug) {
2967
+ console.debug(
2968
+ "%c FluentUI-Extended ",
2969
+ "background: #845EF7; color: white; padding: 2px 4px; border-radius: 2px; font-weight: bold;",
2970
+ message,
2971
+ data || ""
2972
+ );
2973
+ }
2905
2974
  props.onTrace?.(message, data);
2906
- }, [props]);
2975
+ }, [props.debug, props.onTrace]);
2907
2976
  const [loading, setLoading] = React3__namespace.useState(false);
2908
2977
  const [availableFields, setAvailableFields] = React3__namespace.useState(
2909
2978
  props.fields && props.fields.length > 0 ? props.fields : FALLBACK_FIELDS
@@ -3132,6 +3201,9 @@ var QueryBuilder = (props) => {
3132
3201
  if (condition.kind === "relatedEntity") {
3133
3202
  return condition;
3134
3203
  }
3204
+ if (condition.isUnknownField && !availableFields.some((f) => f.id === condition.fieldId)) {
3205
+ return condition;
3206
+ }
3135
3207
  const matchedField = availableFields.find((field) => field.id === condition.fieldId) || fallbackField;
3136
3208
  const operators = getOperatorsForType(matchedField.dataType);
3137
3209
  const nextOperator = operators.some((item) => item.value === condition.operator) ? condition.operator : operators[0]?.value;
@@ -3639,16 +3711,16 @@ var QueryBuilder = (props) => {
3639
3711
  onClick: onOpenUploadDialog
3640
3712
  },
3641
3713
  "Import FetchXML"
3642
- ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: uploadDialogOpen, onOpenChange: (_, data) => setUploadDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { style: { maxWidth: "600px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Import FetchXML"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { style: { display: "flex", flexDirection: "column", gap: "12px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Paste your FetchXML below to rebuild the query:"), /* @__PURE__ */ React3__namespace.createElement(
3714
+ ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: uploadDialogOpen, onOpenChange: (_, data) => setUploadDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { className: styles.dialogSurfaceNarrow }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Import FetchXML"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { className: styles.dialogUploadContent }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Paste your FetchXML below to rebuild the query:"), /* @__PURE__ */ React3__namespace.createElement(
3643
3715
  reactComponents.Textarea,
3644
3716
  {
3645
3717
  placeholder: "<fetch><entity name='account'><filter>...</filter></entity></fetch>",
3646
3718
  value: uploadXmlText,
3647
3719
  onChange: (_, data) => setUploadXmlText(data.value),
3648
- style: { minHeight: "200px", fontFamily: "monospace" },
3720
+ className: styles.monacoTextarea,
3649
3721
  resize: "vertical"
3650
3722
  }
3651
- ), uploadError && /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { style: { color: "var(--colorStatusDangerForeground1)" } }, uploadError)), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "secondary" }, "Cancel")), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary", onClick: onApplyUploadedXml, disabled: !uploadXmlText.trim() }, "Apply"))))), props.showValidateButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3723
+ ), uploadError && /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.uploadErrorText }, uploadError)), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "secondary" }, "Cancel")), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary", onClick: onApplyUploadedXml, disabled: !uploadXmlText.trim() }, "Apply"))))), props.showValidateButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3652
3724
  reactComponents.Button,
3653
3725
  {
3654
3726
  size: "small",
@@ -3657,7 +3729,7 @@ var QueryBuilder = (props) => {
3657
3729
  onClick: onValidate
3658
3730
  },
3659
3731
  "Validate"
3660
- ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: validationDialogOpen, onOpenChange: (_, data) => setValidationDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { style: { maxWidth: "500px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Query Validation"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { style: { display: "flex", flexDirection: "column", gap: "16px" } }, /* @__PURE__ */ React3__namespace.createElement("div", null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", style: { display: "flex", alignItems: "center", gap: "4px" } }, validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query Structure: Valid") : /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "Query Structure: Errors Found")), !validationResult?.isValid && /* @__PURE__ */ React3__namespace.createElement("ul", { className: styles.validationErrorList }, validationResult?.errors.map((error, idx) => /* @__PURE__ */ React3__namespace.createElement("li", { key: idx, className: styles.validationErrorItem }, /* @__PURE__ */ React3__namespace.createElement("strong", null, error.fieldLabel, ":"), " ", error.message)))), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.apiValidationSection }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", style: { display: "flex", alignItems: "center", gap: "4px" } }, "Dynamics 365 API Test"), !validationResult?.apiValidation?.available ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "API validation unavailable \u2014 not running in Dynamics 365 environment.") : !validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Fix query structure errors before testing against the API.") : apiValidating ? /* @__PURE__ */ React3__namespace.createElement("div", { style: { display: "flex", alignItems: "center", gap: "8px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny" }), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Testing query against Dynamics 365...")) : validationResult?.apiValidation?.tested ? validationResult.apiValidation.success ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query executed successfully. ", validationResult.apiValidation.recordCount ?? 0, " record(s) would match.") : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "API Error: ", validationResult.apiValidation.errorMessage) : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Waiting for validation..."))), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary" }, "OK")))))), props.showDeleteAllFiltersButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3732
+ ), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Dialog, { open: validationDialogOpen, onOpenChange: (_, data) => setValidationDialogOpen(data.open) }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogSurface, { className: styles.dialogSurfaceCompact }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogBody, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTitle, null, "Query Validation"), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogContent, { className: styles.dialogValidationContent }, /* @__PURE__ */ React3__namespace.createElement("div", null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", className: styles.validationSectionTitle }, validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query Structure: Valid") : /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "Query Structure: Errors Found")), !validationResult?.isValid && /* @__PURE__ */ React3__namespace.createElement("ul", { className: styles.validationErrorList }, validationResult?.errors.map((error, idx) => /* @__PURE__ */ React3__namespace.createElement("li", { key: idx, className: styles.validationErrorItem }, /* @__PURE__ */ React3__namespace.createElement("strong", null, error.fieldLabel, ":"), " ", error.message)))), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.apiValidationSection }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { weight: "semibold", className: styles.validationSectionTitle }, "Dynamics 365 API Test"), !validationResult?.apiValidation?.available ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "API validation unavailable \u2014 not running in Dynamics 365 environment.") : !validationResult?.isValid ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Fix query structure errors before testing against the API.") : apiValidating ? /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.validationApiRow }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny" }), /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, null, "Testing query against Dynamics 365...")) : validationResult?.apiValidation?.tested ? validationResult.apiValidation.success ? /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationSuccess }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.CheckmarkCircleRegular, { className: styles.validationIcon }), "Query executed successfully. ", validationResult.apiValidation.recordCount ?? 0, " record(s) would match.") : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.validationError }, /* @__PURE__ */ React3__namespace.createElement(reactIcons.WarningRegular, { className: styles.validationIcon }), "API Error: ", validationResult.apiValidation.errorMessage) : /* @__PURE__ */ React3__namespace.createElement(reactComponents.Text, { className: styles.apiUnavailable }, "Waiting for validation..."))), /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogActions, null, /* @__PURE__ */ React3__namespace.createElement(reactComponents.DialogTrigger, { disableButtonEnhancement: true }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Button, { appearance: "primary" }, "OK")))))), props.showDeleteAllFiltersButton !== false && /* @__PURE__ */ React3__namespace.createElement(
3661
3733
  reactComponents.Button,
3662
3734
  {
3663
3735
  size: "small",
@@ -3859,17 +3931,27 @@ var QueryBuilder = (props) => {
3859
3931
  onClick: () => addNestedCondition(group.id, condition.id, nestedFields)
3860
3932
  },
3861
3933
  "Add condition"
3862
- ))), selectedRelated && nestedFields.length === 0 && nestedConditions.length === 0 && /* @__PURE__ */ React3__namespace.createElement("div", { style: { padding: "8px 0", color: "var(--colorNeutralForeground3)", fontSize: "12px" } }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny", label: "Loading fields..." })), !selectedRelated && /* @__PURE__ */ React3__namespace.createElement("div", { style: { padding: "8px 0", color: "var(--colorNeutralForeground3)", fontSize: "12px" } }, "Select a related entity to add filter conditions.")));
3934
+ ))), selectedRelated && nestedFields.length === 0 && nestedConditions.length === 0 && /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.emptyRelatedEntity }, /* @__PURE__ */ React3__namespace.createElement(reactComponents.Spinner, { size: "tiny", label: "Loading fields..." })), !selectedRelated && /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.emptyRelatedEntity })));
3863
3935
  }
3936
+ const isFieldUnknown = condition.isUnknownField && !availableFields.some((f) => f.id === condition.fieldId);
3864
3937
  const selectedField = availableFields.find((field) => field.id === condition.fieldId) || defaultField;
3865
3938
  const operators = getOperatorsForType(selectedField.dataType);
3866
3939
  const isNullOperator = condition.operator === "null" || condition.operator === "notnull";
3867
3940
  const isBetween = condition.operator === "between";
3868
3941
  const conditionRowClass = reactComponents.mergeClasses(
3869
3942
  styles.conditionTreeRow,
3870
- invalidConditionIds.has(condition.id) && styles.conditionInvalid
3943
+ (invalidConditionIds.has(condition.id) || isFieldUnknown) && styles.conditionInvalid
3871
3944
  );
3872
- return /* @__PURE__ */ React3__namespace.createElement("div", { className: conditionRowClass, key: condition.id }, /* @__PURE__ */ React3__namespace.createElement("div", { className: connectorClass }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorLine }), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorBranch })), /* @__PURE__ */ React3__namespace.createElement("div", { className: rowGridClass, role: "row" }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.fieldCell, role: "gridcell" }, /* @__PURE__ */ React3__namespace.createElement(
3945
+ return /* @__PURE__ */ React3__namespace.createElement("div", { className: conditionRowClass, key: condition.id }, /* @__PURE__ */ React3__namespace.createElement("div", { className: connectorClass }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorLine }), /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionConnectorBranch })), isFieldUnknown ? /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.conditionUnknownBanner }, /* @__PURE__ */ React3__namespace.createElement("span", null, "\u26A0"), /* @__PURE__ */ React3__namespace.createElement("span", { className: styles.conditionUnknownText }, "Unknown field ", /* @__PURE__ */ React3__namespace.createElement("strong", null, condition.fieldId), " \u2014 not found in entity metadata. Update or remove this condition before saving."), /* @__PURE__ */ React3__namespace.createElement(
3946
+ reactComponents.Button,
3947
+ {
3948
+ appearance: "subtle",
3949
+ size: "small",
3950
+ className: styles.conditionUnknownButton,
3951
+ onClick: () => removeCondition(group.id, condition.id)
3952
+ },
3953
+ "Remove"
3954
+ )) : /* @__PURE__ */ React3__namespace.createElement("div", { className: rowGridClass, role: "row" }, /* @__PURE__ */ React3__namespace.createElement("div", { className: styles.fieldCell, role: "gridcell" }, /* @__PURE__ */ React3__namespace.createElement(
3873
3955
  Lookup,
3874
3956
  {
3875
3957
  className: styles.compactControl,