diff --git a/Composer/packages/client/__tests__/components/design.test.js b/Composer/packages/client/__tests__/components/design.test.js index e54ee79d87..ead441d63f 100644 --- a/Composer/packages/client/__tests__/components/design.test.js +++ b/Composer/packages/client/__tests__/components/design.test.js @@ -9,7 +9,7 @@ import { dialogs } from '../constants.json'; import { TriggerCreationModal } from './../../src/components/ProjectTree/TriggerCreationModal'; import { ProjectTree } from './../../src/components/ProjectTree'; -import NewDialogModal from './../../src/pages/design/new-dialog-modal'; +import { CreateDialogModal } from './../../src/pages/design/createDialogModal'; describe('', () => { it('should render the ProjectTree', async () => { const dialogId = 'Main'; @@ -36,14 +36,14 @@ describe('', () => { expect(handleSelect).toHaveBeenCalledTimes(1); }); - it('should open NewDialogModal, close after clicking cancel', async () => { + it('should open CreateDialog Modal, close after clicking cancel', async () => { let isOpen = true; const handleDismiss = jest.fn(() => { isOpen = false; }); const handleSubmit = jest.fn(() => {}); const { getByText } = render( - {}} /> + ); const cancelButton = getByText('Cancel'); fireEvent.click(cancelButton); diff --git a/Composer/packages/client/src/CreationFlow/DefineConversation/index.tsx b/Composer/packages/client/src/CreationFlow/DefineConversation/index.tsx index 2ca8ec2d0e..4f2dba132e 100644 --- a/Composer/packages/client/src/CreationFlow/DefineConversation/index.tsx +++ b/Composer/packages/client/src/CreationFlow/DefineConversation/index.tsx @@ -38,7 +38,6 @@ interface DefineConversationProps { onDismiss: () => void; onCurrentPathUpdate?: (newPath?: string, storageId?: string) => void; onGetErrorMessage?: (text: string) => void; - enableLocationBrowse: boolean; focusedStorageFolder?: StorageFolder; currentPath?: string; bots?: Bots[]; @@ -47,15 +46,7 @@ interface DefineConversationProps { const initialFormDataError: FormDataError = {}; export const DefineConversation: React.FC = props => { - const { - onSubmit, - onDismiss, - onCurrentPathUpdate, - enableLocationBrowse, - focusedStorageFolder, - currentPath, - bots, - } = props; + const { onSubmit, onDismiss, onCurrentPathUpdate, focusedStorageFolder, currentPath, bots } = props; const { state } = useContext(StoreContext); const { templateId } = state; @@ -130,7 +121,6 @@ export const DefineConversation: React.FC = props => { const handleSubmit = e => { e.preventDefault(); const errors = validateForm(formData); - if (Object.keys(errors).length) { setFormDataErrors(errors); return; @@ -145,7 +135,7 @@ export const DefineConversation: React.FC = props => {
- + = props => { /> - {enableLocationBrowse && focusedStorageFolder && onCurrentPathUpdate && currentPath && ( + {focusedStorageFolder && onCurrentPathUpdate && currentPath && ( void; + onDismiss: () => void; + onCurrentPathUpdate?: (newPath?: string, storageId?: string) => void; + focusedStorageFolder?: StorageFolder; + currentPath?: string; + isOpen: boolean; +} + +export const CreateDialogModal: React.FC = props => { + const { state } = useContext(StoreContext); + const { dialogs } = state; + const { onSubmit, onDismiss, isOpen } = props; + const initialFormData: DialogFormData = { name: '', description: '' }; + const [formData, setFormData] = useState(initialFormData); + const [formDataErrors, setFormDataErrors] = useState<{ name?: string }>({}); + + const updateForm = field => (e, newValue) => { + setFormData({ + ...formData, + [field]: newValue, + }); + }; + + const nameRegex = /^[a-zA-Z0-9-_.]+$/; + const validateForm = (data: DialogFormData) => { + const errors: { name?: string } = {}; + const { name } = data; + + if (name) { + if (!nameRegex.test(name)) { + errors.name = formatMessage( + 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _' + ); + } + if (dialogs.some(dialog => dialog.id === name)) { + errors.name = formatMessage('Duplicaton of dialog name'); + } + } else { + errors.name = formatMessage('Please input a name'); + } + return errors; + }; + + const handleSubmit = e => { + e.preventDefault(); + const errors = validateForm(formData); + if (Object.keys(errors).length) { + setFormDataErrors(errors); + return; + } + + onSubmit({ + ...formData, + }); + }; + + return ( + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/Composer/packages/client/src/pages/design/index.tsx b/Composer/packages/client/src/pages/design/index.tsx index 21be3dc835..1d38faceb3 100644 --- a/Composer/packages/client/src/pages/design/index.tsx +++ b/Composer/packages/client/src/pages/design/index.tsx @@ -9,7 +9,6 @@ import { Breadcrumb, IBreadcrumbItem } from 'office-ui-fabric-react/lib/Breadcru import { Icon } from 'office-ui-fabric-react/lib/Icon'; import formatMessage from 'format-message'; import { globalHistory } from '@reach/router'; -import toLower from 'lodash/toLower'; import get from 'lodash/get'; import { PromptTab } from '@bfc/shared'; import { getNewDesigner, seedNewDialog } from '@bfc/shared'; @@ -29,7 +28,7 @@ import { clearBreadcrumb } from '../../utils/navigation'; import undoHistory from '../../store/middlewares/undo/history'; import grayComposerIcon from '../../images/grayComposerIcon.svg'; -import NewDialogModal from './new-dialog-modal'; +import { CreateDialogModal } from './createDialogModal'; import { breadcrumbClass, contentWrapper, @@ -190,17 +189,6 @@ function DesignPage(props) { } } - const getErrorMessage = text => { - if ( - dialogs.findIndex(dialog => { - return toLower(dialog.id) === toLower(text); - }) >= 0 - ) { - return `${text} has been used, please choose another name`; - } - return ''; - }; - const onCreateDialogComplete = newDialog => { if (newDialog) { navTo(newDialog); @@ -410,12 +398,13 @@ function DesignPage(props) { - actions.createDialogCancel()} - onSubmit={onSubmit} - onGetErrorMessage={getErrorMessage} - /> + {state.showCreateDialogModal && ( + actions.createDialogCancel()} + onSubmit={onSubmit} + /> + )} {triggerModalVisible && ( void; - onSubmit: (data: { name: string; description: string }) => void; - onGetErrorMessage: (text: string) => string; -} - -const NewDialogModal: React.FC = props => { - const { isOpen, onDismiss, onSubmit, onGetErrorMessage } = props; - - return ( - - - - ); -}; - -export default NewDialogModal; diff --git a/Composer/packages/client/src/pages/design/styles.js b/Composer/packages/client/src/pages/design/styles.js index d957313784..eada79f3ce 100644 --- a/Composer/packages/client/src/pages/design/styles.js +++ b/Composer/packages/client/src/pages/design/styles.js @@ -128,3 +128,55 @@ export const triggerButton = css` font-size: 12px; color: #0078d4; `; + +export const styles = { + dialog: { + title: { + fontWeight: FontWeights.bold, + fontSize: FontSizes.size20, + paddingTop: '14px', + paddingBottom: '11px', + }, + subText: { + fontSize: FontSizes.size14, + }, + }, + modal: { + main: { + maxWidth: '80% !important', + width: '960px !important', + }, + }, + halfstack: { + root: [ + { + flexBasis: '50%', + }, + ], + }, + stackinput: { + root: [ + { + marginBottom: '1rem', + }, + ], + }, +}; + +export const textFieldlabel = { + label: { + root: [ + { + fontWeight: FontWeights.semibold, + }, + ], + }, +}; + +export const name = { + subComponentStyles: textFieldlabel, +}; + +export const description = { + subComponentStyles: textFieldlabel, +};