From f3350afe6a7676d1c4449f3cafdc154542d2a727 Mon Sep 17 00:00:00 2001 From: yann300 Date: Fri, 17 Jun 2022 15:32:41 +0200 Subject: [PATCH] add validationFn to the prompt modal to validate the input --- .../src/app/tabs/abstract-provider.tsx | 14 ++++++++ .../components/modals/modal-wrapper.tsx | 34 +++++++++++++++---- .../src/lib/remix-app/context/provider.tsx | 6 ++-- .../app/src/lib/remix-app/interface/index.ts | 6 ++++ .../app/src/lib/remix-app/reducer/modals.ts | 1 + .../app/src/lib/remix-app/state/modals.ts | 1 + .../src/lib/remix-ui-modal-dialog.tsx | 10 +++--- .../modal-dialog/src/lib/types/index.ts | 7 ++++ 8 files changed, 66 insertions(+), 13 deletions(-) diff --git a/apps/remix-ide/src/app/tabs/abstract-provider.tsx b/apps/remix-ide/src/app/tabs/abstract-provider.tsx index b17f181166..ff924a618a 100644 --- a/apps/remix-ide/src/app/tabs/abstract-provider.tsx +++ b/apps/remix-ide/src/app/tabs/abstract-provider.tsx @@ -58,6 +58,20 @@ export abstract class AbstractProvider extends Plugin { modalType: ModalTypes.prompt, okLabel: 'OK', cancelLabel: 'Cancel', + validationFn: (value) => { + if (!value) return { valid: false, message: "value is empty" } + if (value.startsWith('https://') || value.startsWith('http://')) { + return { + valid: true, + message: '' + } + } else { + return { + valid: false, + message: 'the provided value should contain the protocol ( e.g starts with http:// or https:// )' + } + } + }, okFn: (value: string) => { setTimeout(() => resolve(value), 0) }, diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx index 12644f5fa7..9ee12e871a 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/modal-wrapper.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react' -import { ModalDialog, ModalDialogProps } from '@remix-ui/modal-dialog' +import { ModalDialog, ModalDialogProps, ValidationResult } from '@remix-ui/modal-dialog' import { ModalTypes } from '../../types' interface ModalWrapperProps extends ModalDialogProps { @@ -29,12 +29,25 @@ const ModalWrapper = (props: ModalWrapperProps) => { (props.cancelFn) ? props.cancelFn() : props.resolve(false) } - const createModalMessage = (defaultValue: string) => { + const onInputChanged = (event) => { + if (props.validationFn) { + const validation = props.validationFn(event.target.value) + setState({ + ...props, + message: createModalMessage(props.defaultValue, validation), + validation + }) + } + } + + const createModalMessage = (defaultValue: string, validation: ValidationResult) => { return ( <> {props.message} - - ) + + {!validation.valid && {validation.message}} + + ) } useEffect(() => { @@ -47,7 +60,7 @@ const ModalWrapper = (props: ModalWrapperProps) => { ...props, okFn: onFinishPrompt, cancelFn: onCancelFn, - message: createModalMessage(props.defaultValue) + message: createModalMessage(props.defaultValue, { valid: true }) }) break default: @@ -67,8 +80,17 @@ const ModalWrapper = (props: ModalWrapperProps) => { } }, [props]) + // reset the message and input if any, so when the modal is shown again it doesn't show the previous value. + const handleHide = () => { + setState({ + ...props, + message: '' + }) + props.handleHide() + } + return ( - + ) } export default ModalWrapper diff --git a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx index 2aca27c1be..48f810bea2 100644 --- a/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/context/provider.tsx @@ -16,11 +16,11 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt } const modal = (modalData: AppModal) => { - const { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData + const { id, title, message, validationFn, okLabel, okFn, cancelLabel, cancelFn, modalType, defaultValue, hideFn, data } = modalData return new Promise((resolve, reject) => { dispatch({ type: modalActionTypes.setModal, - payload: { id, title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } + payload: { id, title, message, okLabel, validationFn, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue, hideFn, resolve, next: onNextFn, data } }) }) } @@ -32,7 +32,7 @@ export const ModalProvider = ({ children = [], reducer = modalReducer, initialSt const handleHideModal = () => { dispatch({ type: modalActionTypes.handleHideModal, - payload: null + payload: null, }) } diff --git a/libs/remix-ui/app/src/lib/remix-app/interface/index.ts b/libs/remix-ui/app/src/lib/remix-app/interface/index.ts index f31536678e..cc0521dd51 100644 --- a/libs/remix-ui/app/src/lib/remix-app/interface/index.ts +++ b/libs/remix-ui/app/src/lib/remix-app/interface/index.ts @@ -1,10 +1,16 @@ import { ModalTypes } from '../types' +export type ValidationResult = { + valid: boolean, + message?: string +} + export interface AppModal { id: string timestamp?: number hide?: boolean title: string + validationFn?: (value: string) => ValidationResult // eslint-disable-next-line no-undef message: string | JSX.Element okLabel: string diff --git a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts index a50a3dbd66..51641c654c 100644 --- a/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/reducer/modals.ts @@ -11,6 +11,7 @@ export const modalReducer = (state: ModalState = ModalInitialState, action: Moda id: action.payload.id || timestamp.toString(), hide: false, title: action.payload.title, + validationFn: action.payload.validationFn, message: action.payload.message, okLabel: action.payload.okLabel, okFn: action.payload.okFn, diff --git a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts index 8332d60120..2b10dccbea 100644 --- a/libs/remix-ui/app/src/lib/remix-app/state/modals.ts +++ b/libs/remix-ui/app/src/lib/remix-app/state/modals.ts @@ -8,6 +8,7 @@ export const ModalInitialState: ModalState = { hide: true, title: '', message: '', + validationFn: () => { return {valid: true, message: ''} }, okLabel: '', okFn: () => { }, cancelLabel: '', diff --git a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx index 38df2fd8b8..936956c62a 100644 --- a/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx +++ b/libs/remix-ui/modal-dialog/src/lib/remix-ui-modal-dialog.tsx @@ -96,18 +96,20 @@ export const ModalDialog = (props: ModalDialogProps) => {
{/* todo add autofocus ^^ */} - { props.okLabel && { + if (props.validation && !props.validation.valid) return if (props.okFn) props.okFn() handleHide() }} > {props.okLabel ? props.okLabel : 'OK'} - + } - { props.cancelLabel && { }} > {props.cancelLabel ? props.cancelLabel : 'Cancel'} - + }
diff --git a/libs/remix-ui/modal-dialog/src/lib/types/index.ts b/libs/remix-ui/modal-dialog/src/lib/types/index.ts index a8793971d7..f1ec8c88d9 100644 --- a/libs/remix-ui/modal-dialog/src/lib/types/index.ts +++ b/libs/remix-ui/modal-dialog/src/lib/types/index.ts @@ -1,8 +1,15 @@ +export type ValidationResult = { + valid: boolean, + message?: string +} + /* eslint-disable no-undef */ export interface ModalDialogProps { id: string timestamp?: number, title?: string, + validation?: ValidationResult + validationFn?: (value: string) => ValidationResult message?: string | JSX.Element, okLabel?: string, okFn?: (value?:any) => void,