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} - /> - -