@openg2p/registry-widgets 0.1.4 → 0.1.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.
@@ -16,6 +16,8 @@ export interface SectionRendererProps {
16
16
  hideEditButton?: boolean;
17
17
  mode?: SectionMode;
18
18
  namespace?: string;
19
+ changeRequestType?: 'new' | 'old';
20
+ showChangeRequestLabel?: boolean;
19
21
  }
20
22
  /**
21
23
  * Renders a section with its panels
@@ -25,5 +27,5 @@ export interface SectionRendererProps {
25
27
  * - Panels wrap when they exceed available width
26
28
  * - Sections can sit side-by-side if there's space
27
29
  */
28
- export declare const SectionRenderer: ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton, mode, namespace, }: SectionRendererProps) => import("react/jsx-runtime").JSX.Element;
30
+ export declare const SectionRenderer: ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton, mode, namespace, changeRequestType, showChangeRequestLabel, }: SectionRendererProps) => import("react/jsx-runtime").JSX.Element;
29
31
  //# sourceMappingURL=SectionRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SectionRenderer.d.ts","sourceRoot":"","sources":["../../src/components/SectionRenderer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAyC,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAM9D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIlD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,wBAAwB,CAAC,EAAE,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;IAC5E,UAAU,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CAEpB;AAGD;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,iKAU7B,oBAAoB,4CA07BtB,CAAC"}
1
+ {"version":3,"file":"SectionRenderer.d.ts","sourceRoot":"","sources":["../../src/components/SectionRenderer.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAyC,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAM9D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIlD,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,wBAAwB,CAAC,EAAE,oBAAoB,CAAC,0BAA0B,CAAC,CAAC;IAC5E,UAAU,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAChD,aAAa,CAAC,EAAE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAClE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAClC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAElC;AAGD;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,4MAY7B,oBAAoB,4CA69BtB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"WidgetRenderer.d.ts","sourceRoot":"","sources":["../../src/components/WidgetRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAsB,MAAM,UAAU,CAAC;AAChE,OAAO,EAAiB,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAO7E,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC;IAC/E,MAAM,EAAE,gBAAgB,CAAC;IACzB,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;CAC7C;AAED,eAAO,MAAM,cAAc,GAAI,kIAM5B,mBAAmB,mDAoDrB,CAAC"}
1
+ {"version":3,"file":"WidgetRenderer.d.ts","sourceRoot":"","sources":["../../src/components/WidgetRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAE,gBAAgB,EAAsB,MAAM,UAAU,CAAC;AAChE,OAAO,EAAiB,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAO7E,MAAM,WAAW,mBAAoB,SAAQ,IAAI,CAAC,oBAAoB,EAAE,QAAQ,CAAC;IAC/E,MAAM,EAAE,gBAAgB,CAAC;IACzB,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;CAC7C;AAED,eAAO,MAAM,cAAc,GAAI,kIAM5B,mBAAmB,mDAyDrB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useBaseWidget.d.ts","sourceRoot":"","sources":["../../src/hooks/useBaseWidget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAetE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,gBAAgB,CAAC;IACzB,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACxD;AAMD,eAAO,MAAM,aAAa,GAAI,SAAS,oBAAoB;;;;;;;;;yBAiG5C,GAAG,aAAY,OAAO;;uBA0Qd,MAAM,EAAE;;;;;;;0BAtKpB,MAAM;;;CA2KhB,CAAC"}
1
+ {"version":3,"file":"useBaseWidget.d.ts","sourceRoot":"","sources":["../../src/hooks/useBaseWidget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAetE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,gBAAgB,CAAC;IACzB,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;CACxD;AAMD,eAAO,MAAM,aAAa,GAAI,SAAS,oBAAoB;;;;;;;;;yBAwI5C,GAAG,aAAY,OAAO;;uBA0Qd,MAAM,EAAE;;;;;;;0BAtKpB,MAAM;;;CA2KhB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -543,6 +543,8 @@ interface SectionRendererProps {
543
543
  hideEditButton?: boolean;
544
544
  mode?: SectionMode;
545
545
  namespace?: string;
546
+ changeRequestType?: 'new' | 'old';
547
+ showChangeRequestLabel?: boolean;
546
548
  }
547
549
  /**
548
550
  * Renders a section with its panels
@@ -552,7 +554,7 @@ interface SectionRendererProps {
552
554
  * - Panels wrap when they exceed available width
553
555
  * - Sections can sit side-by-side if there's space
554
556
  */
555
- declare const SectionRenderer: ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton, mode, namespace, }: SectionRendererProps) => react_jsx_runtime.JSX.Element;
557
+ declare const SectionRenderer: ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton, mode, namespace, changeRequestType, showChangeRequestLabel, }: SectionRendererProps) => react_jsx_runtime.JSX.Element;
556
558
 
557
559
  interface SectionBuilderProps {
558
560
  initialSection?: SectionConfig;
package/dist/index.esm.js CHANGED
@@ -950,32 +950,63 @@ const useBaseWidget = (options) => {
950
950
  if (isLayoutWidget) {
951
951
  return undefined; // Layout widgets don't have values
952
952
  }
953
+ // Helper to extract displayable value from object (especially geo hierarchy objects)
954
+ const extractValueFromObject = (obj) => {
955
+ if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
956
+ return obj;
957
+ }
958
+ // Check for geo hierarchy structure first
959
+ if ('geo_code_hierarchy_json' in obj || 'geo_lowest_level_value_id' in obj) {
960
+ if ('geo_lowest_level_value_id' in obj) {
961
+ return obj.geo_lowest_level_value_id;
962
+ }
963
+ // If it's a geo hierarchy object but no extractable ID, return undefined to avoid rendering object
964
+ return undefined;
965
+ }
966
+ // Try common value fields
967
+ if ('value' in obj) {
968
+ return obj.value;
969
+ }
970
+ if ('id' in obj) {
971
+ return obj.id;
972
+ }
973
+ if ('label' in obj) {
974
+ return obj.label;
975
+ }
976
+ if ('name' in obj) {
977
+ return obj.name;
978
+ }
979
+ // If no extractable value found, return undefined to avoid rendering object as React child
980
+ // This prevents "Objects are not valid as a React child" errors
981
+ return undefined;
982
+ };
953
983
  // Try to get value from widgetId first (this should have the actual selected value)
954
984
  // For geo widgets with dataPath, widgetId stores the actual ID, while dataPath stores the hierarchy object
955
985
  let value = values[widgetId];
986
+ // Extract value if it's an object (handles geo hierarchy objects stored in widgetId)
987
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
988
+ value = extractValueFromObject(value);
989
+ }
956
990
  // If widgetId doesn't have a value, try dataPath
957
991
  if (value === undefined && config['widget-data-path']) {
958
992
  value = getWidgetValue(values, config['widget-data-path'], widgetId);
959
- // CRITICAL: For geo widgets with dataPath, the value might be stored as a hierarchy object
960
- // Extract the actual value (geo_lowest_level_value_id) if it's an object
961
- const geoConfig = config['widget-geo-config'];
962
- if (geoConfig?.isLastLevel && value && typeof value === 'object' && !Array.isArray(value)) {
963
- // If it's a geo hierarchy object, extract the actual value
964
- if ('geo_lowest_level_value_id' in value) {
965
- value = value.geo_lowest_level_value_id;
966
- }
967
- else if ('value' in value) {
968
- value = value.value;
969
- }
970
- else if ('id' in value) {
971
- value = value.id;
972
- }
993
+ // Extract value if it's an object (handles geo hierarchy objects from dataPath)
994
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
995
+ value = extractValueFromObject(value);
973
996
  }
974
997
  }
975
998
  // If value is still undefined and user has set a value, try reading from widgetId as backup
976
999
  // This handles cases where dataPath lookup might fail temporarily
977
1000
  if (value === undefined && userHasSetValueRef.current && values[widgetId] !== undefined) {
978
1001
  value = values[widgetId];
1002
+ // Extract value if it's an object (handles geo hierarchy objects)
1003
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
1004
+ value = extractValueFromObject(value);
1005
+ }
1006
+ }
1007
+ // Final safety check: if value is still an object, extract displayable value
1008
+ if (value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value)) {
1009
+ value = extractValueFromObject(value);
979
1010
  }
980
1011
  // If user has explicitly set a value, always return it (even if undefined/null)
981
1012
  // This prevents the default from overwriting user selections
@@ -2245,7 +2276,7 @@ const WidgetProvider = ({ store, dataSourceRequestHandler, schemaData, translate
2245
2276
  widgetStore.dispatch(setValues(schemaData));
2246
2277
  }
2247
2278
  // eslint-disable-next-line react-hooks/exhaustive-deps
2248
- }, []); // Only run on mount
2279
+ }, [schemaData, widgetStore]); // Only run on mount
2249
2280
  const content = (jsxRuntimeExports.jsx(Provider, { store: widgetStore, children: jsxRuntimeExports.jsx(WidgetContext.Provider, { value: contextValue, children: jsxRuntimeExports.jsx(WidgetEventBusContext.Provider, { value: eventBus, children: children }) }) }));
2250
2281
  return content;
2251
2282
  };
@@ -2255,8 +2286,11 @@ const WidgetRenderer = ({ config, dataSourceRequestHandler: propDataSourceReques
2255
2286
  const context = useWidgetContext();
2256
2287
  const dataSourceRequestHandler = propDataSourceRequestHandler || context.dataSourceRequestHandler;
2257
2288
  const schemaData = propSchemaData || context.schemaData;
2289
+ // Warn if dataSourceRequestHandler is missing for API data sources, but don't break rendering
2290
+ // This allows widgets to render in read-only or static modes (e.g., CRView)
2258
2291
  if (!dataSourceRequestHandler && config['widget-data-source']?.type === 'api') {
2259
- console.error(`[WidgetRenderer] dataSourceRequestHandler is required for widget ${config['widget-id']} with API data source`);
2292
+ console.warn(`[WidgetRenderer] dataSourceRequestHandler is not provided for widget ${config['widget-id']} with API data source. ` +
2293
+ `The widget will render but API data source functionality will be disabled.`);
2260
2294
  }
2261
2295
  // Get values from Redux for cascade hooks
2262
2296
  const values = useSelector((state) => state.widget.values);
@@ -3192,7 +3226,7 @@ const namespaceSectionConfig = (section, namespace) => {
3192
3226
  * - Panels wrap when they exceed available width
3193
3227
  * - Sections can sit side-by-side if there's space
3194
3228
  */
3195
- const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, }) => {
3229
+ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequestHandler, schemaData, onValueChange, gridColumnSpan, onSectionSave, hideEditButton = false, mode = 'RegistryView', namespace, changeRequestType, showChangeRequestLabel = true, }) => {
3196
3230
  const { translateConfig, translate } = useWidgetTranslation();
3197
3231
  const { schemaData: contextSchemaData, dataSourceRequestHandler: contextDataSourceRequestHandler } = useWidgetContext();
3198
3232
  const store = useStore();
@@ -3810,11 +3844,12 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
3810
3844
  align-items: center;
3811
3845
  gap: 0.5rem;
3812
3846
  }
3813
- ` }), jsxRuntimeExports.jsxs("div", { ref: sectionRef, className: `section ${sectionClassId} px-4 sm:px-6 lg:px-8 border-2 border-white `, "data-section-id": sectionId, "data-has-table": hasTableWidget ? 'true' : 'false', "data-has-explicit-span": hasExplicitTableSpan ? 'true' : 'false', "data-edit-mode": isEditMode ? 'true' : 'false', "data-column-span": columnSpan, style: {
3847
+ ` }), jsxRuntimeExports.jsxs("div", { ref: sectionRef, className: `section ${sectionClassId} px-4 sm:px-6 lg:px-8 border-2 border-white `, "data-section-id": sectionId, "data-has-table": hasTableWidget ? 'true' : 'false', "data-has-explicit-span": hasExplicitTableSpan ? 'true' : 'false', "data-edit-mode": isEditMode ? 'true' : 'false', "data-column-span": columnSpan, "data-change-request-type": changeRequestType, style: {
3814
3848
  gridColumn: `span ${columnSpan}`,
3815
3849
  width: '100%',
3816
3850
  borderRadius: '10px',
3817
- backgroundColor: '#FFFFFF',
3851
+ backgroundColor: changeRequestType === 'old' ? '#F9F9F9' : '#FFFFFF', // Faded background for old change requests
3852
+ opacity: changeRequestType === 'old' ? 0.95 : 1, // Slight opacity reduction for old sections
3818
3853
  ...(isEditMode && sectionHeight ? {
3819
3854
  height: `${sectionHeight}px`,
3820
3855
  minHeight: `${sectionHeight}px`
@@ -3823,7 +3858,27 @@ const SectionRenderer = ({ section, dataSourceRequestHandler: propDataSourceRequ
3823
3858
  minHeight: 'auto',
3824
3859
  height: 'auto'
3825
3860
  }),
3826
- }, children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold mb-4", style: { marginTop: '35px' }, children: translateConfig(sectionToRender['section-title']) })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && hideEditButton ? { paddingBottom: '40px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, dataSourceRequestHandler: dataSourceRequestHandler, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "border-gray-300 w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
3861
+ }, children: [sectionToRender['section-title'] && (jsxRuntimeExports.jsxs("div", { style: {
3862
+ marginTop: '35px',
3863
+ marginBottom: '16px',
3864
+ display: 'flex',
3865
+ alignItems: 'center',
3866
+ gap: '12px',
3867
+ flexWrap: 'wrap',
3868
+ }, children: [jsxRuntimeExports.jsx("h2", { className: "text-xl font-semibold", style: { margin: 0 }, children: translateConfig(sectionToRender['section-title']) }), mode === 'CRView' && changeRequestType && showChangeRequestLabel && (jsxRuntimeExports.jsx("span", { style: {
3869
+ display: 'inline-flex',
3870
+ alignItems: 'center',
3871
+ padding: '4px 12px',
3872
+ borderRadius: '4px',
3873
+ fontSize: '12px',
3874
+ fontWeight: 600,
3875
+ textTransform: 'uppercase',
3876
+ letterSpacing: '0.5px',
3877
+ backgroundColor: changeRequestType === 'new' ? '#28a745' : '#ffcccc', // Green for new, faded red for old
3878
+ color: changeRequestType === 'new' ? '#FFFFFF' : '#cc0000',
3879
+ whiteSpace: 'nowrap',
3880
+ boxShadow: changeRequestType === 'new' ? '0 2px 4px rgba(40, 167, 69, 0.3)' : 'none',
3881
+ }, children: changeRequestType === 'new' ? 'New' : 'Old' }))] })), jsxRuntimeExports.jsxs("div", { id: gridId, className: "section-panels", style: mode === 'RegistryView' && hideEditButton ? { paddingBottom: '40px' } : {}, children: [editableSection.panels.map((panel, index) => (jsxRuntimeExports.jsx("div", { className: "panel-wrapper", children: jsxRuntimeExports.jsx(PanelRenderer, { panel: panel, dataSourceRequestHandler: dataSourceRequestHandler, schemaData: namespacedSchemaData, onValueChange: onValueChange }) }, panel['panel-id'] || `section-panel-${index}`))), mode === 'CRView' && crViewData && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("hr", { className: "border-gray-300 w-full", style: { height: '1px', marginTop: '20px', marginBottom: '0px' } }), jsxRuntimeExports.jsxs("div", { className: "cr-view-container", style: {
3827
3882
  marginTop: '20px',
3828
3883
  paddingBottom: '30px',
3829
3884
  display: 'flex',
@@ -7396,8 +7451,41 @@ const CurrencyInputWidget = ({ config }) => {
7396
7451
  const DisplayWidget = ({ config }) => {
7397
7452
  const { value, formattedValue, config: widgetConfig, } = useBaseWidget({ config });
7398
7453
  const { translateConfig } = useWidgetTranslation();
7399
- // Use formatted value if available, otherwise raw value
7400
- const displayValue = formattedValue !== undefined ? formattedValue : (value || '');
7454
+ // Helper to safely convert value to display string
7455
+ const getDisplayValue = (val) => {
7456
+ if (val === null || val === undefined) {
7457
+ return '';
7458
+ }
7459
+ // If it's already a string or number, return as string
7460
+ if (typeof val === 'string' || typeof val === 'number') {
7461
+ return String(val);
7462
+ }
7463
+ // If it's an object (like geo hierarchy), try to extract a meaningful value
7464
+ if (typeof val === 'object' && !Array.isArray(val)) {
7465
+ // For geo hierarchy objects, try to extract the actual value
7466
+ if ('geo_lowest_level_value_id' in val) {
7467
+ return String(val.geo_lowest_level_value_id || '');
7468
+ }
7469
+ if ('value' in val) {
7470
+ return String(val.value || '');
7471
+ }
7472
+ if ('id' in val) {
7473
+ return String(val.id || '');
7474
+ }
7475
+ // If no extractable value, return empty string to avoid rendering object
7476
+ return '';
7477
+ }
7478
+ // For arrays, join them or return empty
7479
+ if (Array.isArray(val)) {
7480
+ return val.length > 0 ? val.map(String).join(', ') : '';
7481
+ }
7482
+ // Fallback: convert to string
7483
+ return String(val);
7484
+ };
7485
+ // Use formatted value if available, otherwise safely convert raw value
7486
+ const displayValue = formattedValue !== undefined
7487
+ ? (typeof formattedValue === 'object' ? getDisplayValue(formattedValue) : String(formattedValue))
7488
+ : getDisplayValue(value);
7401
7489
  const label = translateConfig(widgetConfig['widget-label']);
7402
7490
  // If no label, render as paragraph text
7403
7491
  if (!label || label.trim() === '') {