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 ab9ef69223e1..881774ba2c74 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,47 @@ 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 +95,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 +116,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) { ); pairs.length !== 0 && !typing && setRenderPairs(newRenderPairs); + validatePairs(newRenderPairs); }, [props, pairs.length, renderPairs.length]); const debouncedUpdatePairs = useCallback( @@ -105,6 +138,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) { setRenderPairs(updatedRenderPairs); debouncedUpdatePairs(updatedPairs); + validatePairs(updatedRenderPairs); } function updateValue(index: number, updatedValue: string) { @@ -119,6 +153,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 +174,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) { const newRenderPairs = renderPairs.filter((o, i) => i !== index); setRenderPairs(newRenderPairs); + validatePairs(newRenderPairs); props.updatePairs(newPairs, isUpdatedViaKeyboard); } @@ -162,6 +208,7 @@ export function KeyValueComponent(props: KeyValueComponentProps) { }); setRenderPairs(updatedRenderPairs); + validatePairs(updatedRenderPairs); props.updatePairs(pairs, e.detail === 0); } @@ -176,41 +223,52 @@ export function KeyValueComponent(props: KeyValueComponentProps) { return ( <> {renderPairs.map((pair: SegmentedControlOptionWithKey, index) => { + const hasError = !!errorMessages[index]; return ( - - { - updateKey(index, value); - }} - onFocus={onInputFocus} - placeholder={"Name"} - value={pair.label} - /> - - { - updateValue(index, value); - }} - onFocus={onInputFocus} - placeholder={"Value"} - value={pair.value} - /> - -