From f8c235d25b9274aeb8abadb60c510f8c4bcbe5c8 Mon Sep 17 00:00:00 2001 From: Manish Kushare Date: Fri, 24 Mar 2023 18:59:35 +0530 Subject: [PATCH] Tree component is not reflecting data correctly (#5759) * bug fixed: tree select is getting crashed, when data,checkedData,expandedData props are not valid * bug fixed: tabs component is crashing when in valid data provided for the tabs property * removed unwanted commented line * bug fixed for button group component * bug fixed for steps components * app is becoming unresponsive for invalid data type in labels property of Button group widget * app is becoming unresponsive for invalid data type in currentStep property of Steps widget * made sugggested changes * new buttons are getting added to the button groups if labels length exceeds values length * reverting back last commit * removing unwanted commented code --- .../src/Editor/Components/ButtonGroup.jsx | 10 +++++-- frontend/src/Editor/Components/Steps.jsx | 5 +++- frontend/src/Editor/Components/Tabs.jsx | 4 +-- frontend/src/Editor/Components/TreeSelect.jsx | 7 +++-- frontend/src/_helpers/utils.js | 27 +++++++++++++++++++ 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/frontend/src/Editor/Components/ButtonGroup.jsx b/frontend/src/Editor/Components/ButtonGroup.jsx index 9643f25638..0cb6b83e05 100644 --- a/frontend/src/Editor/Components/ButtonGroup.jsx +++ b/frontend/src/Editor/Components/ButtonGroup.jsx @@ -1,4 +1,6 @@ import React, { useEffect, useState } from 'react'; +import { isExpectedDataType } from '@/_helpers/utils'; +import _ from 'lodash'; export const ButtonGroup = function Button({ height, @@ -9,7 +11,11 @@ export const ButtonGroup = function Button({ darkMode, dataCy, }) { - const { values, labels, label, defaultSelected, multiSelection } = properties; + const { label, multiSelection } = properties; + const values = isExpectedDataType(properties.values, 'array'); + const labels = isExpectedDataType(properties.labels, 'array'); + const defaultSelected = isExpectedDataType(properties.defaultSelected, 'array'); + const { backgroundColor, textColor, @@ -45,7 +51,7 @@ export const ButtonGroup = function Button({ } else { setData(labels); } - }, [labels, values]); + }, [JSON.stringify(labels), JSON.stringify(values)]); useEffect(() => { setDefaultActive(defaultSelected); diff --git a/frontend/src/Editor/Components/Steps.jsx b/frontend/src/Editor/Components/Steps.jsx index c2ae92552d..e023ab6142 100644 --- a/frontend/src/Editor/Components/Steps.jsx +++ b/frontend/src/Editor/Components/Steps.jsx @@ -1,7 +1,10 @@ import React, { useEffect, useState } from 'react'; +import { isExpectedDataType } from '@/_helpers/utils'; export const Steps = function Button({ properties, styles, fireEvent, setExposedVariable, height, darkMode, dataCy }) { - const { currentStep, stepsSelectable, steps } = properties; + const { stepsSelectable } = properties; + const currentStep = isExpectedDataType(properties.currentStep, 'number'); + const steps = isExpectedDataType(properties.steps, 'array'); const { color, theme, visibility } = styles; const textColor = darkMode && styles.textColor === '#000' ? '#fff' : styles.textColor; const [activeStep, setActiveStep] = useState(null); diff --git a/frontend/src/Editor/Components/Tabs.jsx b/frontend/src/Editor/Components/Tabs.jsx index bdcc65b369..ce9a764e12 100644 --- a/frontend/src/Editor/Components/Tabs.jsx +++ b/frontend/src/Editor/Components/Tabs.jsx @@ -1,7 +1,7 @@ import React, { useRef, useState, useEffect } from 'react'; import { SubCustomDragLayer } from '../SubCustomDragLayer'; import { SubContainer } from '../SubContainer'; -import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils'; +import { resolveReferences, resolveWidgetFieldValue, isExpectedDataType } from '@/_helpers/utils'; export const Tabs = function Tabs({ id, @@ -24,7 +24,7 @@ export const Tabs = function Tabs({ const disabledState = component.definition.styles?.disabledState?.value ?? false; const defaultTab = component.definition.properties.defaultTab.value; // config for tabs. Includes title - const tabs = component.definition.properties?.tabs?.value ?? []; + const tabs = isExpectedDataType(resolveReferences(component.definition.properties.tabs.value, currentState), 'array'); let parsedTabs = tabs; parsedTabs = resolveWidgetFieldValue(parsedTabs, currentState); const hideTabs = component.definition.properties?.hideTabs?.value ?? false; diff --git a/frontend/src/Editor/Components/TreeSelect.jsx b/frontend/src/Editor/Components/TreeSelect.jsx index 6d9517dd6e..0db2b789d9 100644 --- a/frontend/src/Editor/Components/TreeSelect.jsx +++ b/frontend/src/Editor/Components/TreeSelect.jsx @@ -3,14 +3,17 @@ import React, { useState, useEffect, useMemo } from 'react'; import CheckboxTree from 'react-checkbox-tree'; // eslint-disable-next-line import/no-unresolved import 'react-checkbox-tree/lib/react-checkbox-tree.css'; +import { isExpectedDataType } from '@/_helpers/utils.js'; export const TreeSelect = ({ height, properties, styles, setExposedVariable, fireEvent, darkMode, dataCy }) => { - const { label, data, checkedData, expandedData } = properties; + const { label } = properties; const { visibility, disabledState, checkboxColor } = styles; const textColor = darkMode && styles.textColor === '#000' ? '#fff' : styles.textColor; const [checked, setChecked] = useState(checkedData); const [expanded, setExpanded] = useState(expandedData); - + const data = isExpectedDataType(properties.data, 'array'); + const checkedData = isExpectedDataType(properties.checkedData, 'array'); + const expandedData = isExpectedDataType(properties.expandedData, 'array'); let pathObj = {}; useEffect(() => { diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js index fb444c421c..6301a4d7a8 100644 --- a/frontend/src/_helpers/utils.js +++ b/frontend/src/_helpers/utils.js @@ -666,3 +666,30 @@ export const getuserName = (formData) => { return `${nameArray?.[0][0]}${nameArray?.[1] != undefined && nameArray?.[1] != '' ? nameArray?.[1][0] : ''} `; return ''; }; + +export function isExpectedDataType(data, expectedDataType) { + function getCurrentDataType(node) { + return Object.prototype.toString.call(node).slice(8, -1).toLowerCase(); + } + + const currentDataType = getCurrentDataType(data); + + if (currentDataType !== expectedDataType) { + switch (expectedDataType) { + case 'string': + return String(data) ? data : undefined; + case 'number': + return Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === 'number' ? data : undefined; + case 'boolean': + return Boolean(); + case 'array': + return Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === 'array' ? data : []; + case 'object': + return Object.prototype.toString.call(data).slice(8, -1).toLowerCase() === 'object' ? data : {}; + default: + return null; + } + } + + return data; +}