diff --git a/app/client/cypress/e2e/Regression/ClientSide/IDE/Canvas_Context_Bug_Fixes.js b/app/client/cypress/e2e/Regression/ClientSide/IDE/Canvas_Context_Bug_Fixes.js
index 341a83185c6f..69f0fbaf8d3b 100644
--- a/app/client/cypress/e2e/Regression/ClientSide/IDE/Canvas_Context_Bug_Fixes.js
+++ b/app/client/cypress/e2e/Regression/ClientSide/IDE/Canvas_Context_Bug_Fixes.js
@@ -27,4 +27,4 @@ describe("Canvas context Property Pane", { tags: ["@tag.IDE"] }, function () {
//check if the entities are expanded
cy.get(`[data-guided-tour-id="explorer-entity-Image1"]`).should("exist");
});
-});
+});
\ No newline at end of file
diff --git a/app/client/src/assets/icons/alert/warning-error.svg b/app/client/src/assets/icons/alert/warning-error.svg
new file mode 100644
index 000000000000..69d06d875b24
--- /dev/null
+++ b/app/client/src/assets/icons/alert/warning-error.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/client/src/components/propertyControls/KeyValueComponent.tsx b/app/client/src/components/propertyControls/KeyValueComponent.tsx
index 672ff159d680..a7a9ab5c1e29 100644
--- a/app/client/src/components/propertyControls/KeyValueComponent.tsx
+++ b/app/client/src/components/propertyControls/KeyValueComponent.tsx
@@ -6,6 +6,7 @@ import { Button } from "design-system";
import { generateReactKey } from "utils/generators";
import { debounce } from "lodash";
import { getNextEntityName } from "utils/AppsmithUtils";
+import { ReactComponent as WarningErrorIcon } from "assets/icons/alert/warning-error.svg";
function updateOptionLabel(
options: Array,
@@ -38,11 +39,51 @@ function updateOptionValue(
};
});
}
+const FlexBox = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+`;
+
+const ErrorMessageBox = styled.div`
+ color: ${(props) => props.theme.colors.error};
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+ align-items: center;
+ justify-content: center;
+ margin-left: 0;
+ margin-bottom: 12px;
+`;
const StyledBox = styled.div`
width: 10px;
`;
+const StyledInputGroup = styled(InputGroup)<{ hasError: boolean }>`
+ > .ads-v2-input__input-section > div {
+ min-width: 0px;
+ }
+ & input {
+ ${(props) =>
+ props.hasError &&
+ `
+ border-color: ${props.theme.colors.error};
+ `}
+ ${(props) =>
+ !props.hasError &&
+ `
+ border-color: #cdd5df;
+ &:focus {
+ border-color: #4c5664;
+ }
+ &:hover {
+ border-color: #99a4b3;
+ }
+ `}
+ }
+`;
+
type UpdatePairFunction = (
pair: SegmentedControlOption[],
isUpdatedViaKeyboard?: boolean,
@@ -58,17 +99,12 @@ type SegmentedControlOptionWithKey = SegmentedControlOption & {
key: string;
};
-const StyledInputGroup = styled(InputGroup)`
- > .ads-v2-input__input-section > div {
- min-width: 0px;
- }
-`;
-
export function KeyValueComponent(props: KeyValueComponentProps) {
const [renderPairs, setRenderPairs] = useState<
SegmentedControlOptionWithKey[]
>([]);
const [typing, setTyping] = useState(false);
+ const [errorMessages, setErrorMessages] = useState([]);
const { pairs } = props;
useEffect(() => {
let { pairs } = props;
@@ -84,6 +120,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) {
);
pairs.length !== 0 && !typing && setRenderPairs(newRenderPairs);
+ validatePairs(newRenderPairs);
}, [props, pairs.length, renderPairs.length]);
const debouncedUpdatePairs = useCallback(
@@ -105,6 +142,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) {
setRenderPairs(updatedRenderPairs);
debouncedUpdatePairs(updatedPairs);
+ validatePairs(updatedRenderPairs);
}
function updateValue(index: number, updatedValue: string) {
@@ -119,6 +157,17 @@ export function KeyValueComponent(props: KeyValueComponentProps) {
setRenderPairs(updatedRenderPairs);
debouncedUpdatePairs(updatedPairs);
+ validatePairs(updatedRenderPairs);
+ }
+
+ function validatePairs(pairs: SegmentedControlOptionWithKey[]) {
+ const newErrorMessages = pairs.map((pair) => {
+ if (!pair.label && !pair.value) {
+ return "Both Name and Value can't be empty";
+ }
+ return "";
+ });
+ setErrorMessages(newErrorMessages);
}
function deletePair(index: number, isUpdatedViaKeyboard = false) {
@@ -129,6 +178,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) {
const newRenderPairs = renderPairs.filter((o, i) => i !== index);
setRenderPairs(newRenderPairs);
+ validatePairs(newRenderPairs);
props.updatePairs(newPairs, isUpdatedViaKeyboard);
}
@@ -162,6 +212,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) {
});
setRenderPairs(updatedRenderPairs);
+ validatePairs(updatedRenderPairs);
props.updatePairs(pairs, e.detail === 0);
}
@@ -176,42 +227,52 @@ export function KeyValueComponent(props: KeyValueComponentProps) {
return (
<>
{renderPairs.map((pair: SegmentedControlOptionWithKey, index) => {
+ const hasError = !!errorMessages[index];
return (
-
- {
- updateKey(index, value);
- }}
- onFocus={onInputFocus}
- placeholder={"Name"}
- // @ts-expect-error fix this the next time the file is edited
- value={pair.label}
- />
-
- {
- updateValue(index, value);
- }}
- onFocus={onInputFocus}
- placeholder={"Value"}
- value={pair.value}
- />
-
-
+
+
+ {
+ updateKey(index, value);
+ }}
+ onFocus={onInputFocus}
+ placeholder={"Name"}
+ value={pair.label}
+ />
+
+ {
+ updateValue(index, value);
+ }}
+ onFocus={onInputFocus}
+ placeholder={"Value"}
+ value={pair.value}
+ />
+
+
+ {errorMessages[index] && (
+
+
+ {errorMessages[index]}
+
+ )}
+
);
})}
diff --git a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx
index 15fe153a9955..2da4610337cf 100644
--- a/app/client/src/widgets/RadioGroupWidget/widget/index.tsx
+++ b/app/client/src/widgets/RadioGroupWidget/widget/index.tsx
@@ -70,6 +70,16 @@ export function optionsCustomValidation(
break;
}
+ if (!label && !value) {
+ _isValid = false;
+ message = {
+ name: "ValidationError",
+ message:
+ "Both Name and Value can't be empty",
+ };
+ break;
+ }
+
//Check if the required field "label" is present:
if (!label) {
_isValid = false;