Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useState, useContext, useEffect, useRef, Fragment } from 'react';
import React, { useState, useContext, useEffect, useRef } from 'react';
import { DialogFooter, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import formatMessage from 'format-message';
import { DialogFooter, PrimaryButton, DefaultButton, Stack, StackItem, TextField } from 'office-ui-fabric-react';

import { DialogModal } from '../../CreationFlow/DialogModal/index';
import { LocationSelectContent } from '../LocationBrowser/LocationSelectContent';
import { styles as wizardStyles } from '../StepWizard/styles';

import { StoreContext } from './../../store';
import { name, description } from './styles';

const nameRegex = /^[a-zA-Z0-9-_.]+$/;

Expand All @@ -23,16 +22,14 @@ const validateForm = data => {
return errors;
};

export function DefineConversation(props) {
const { onSubmit, onGetErrorMessage, onDismiss, enableLocationBrowse } = props;
export default function DefineConversation(props) {
const { onDismiss, onSubmit, onGetErrorMessage } = props;
const [formData, setFormData] = useState({ errors: {} });
const [disable, setDisable] = useState(false);
const { state } = useContext(StoreContext);
const { storages } = state;
const currentStorageIndex = useRef(0);

const [formData, setFormData] = useState({ errors: {} });
const [disable, setDisable] = useState(false);

// set the default path
useEffect(() => {
const index = currentStorageIndex.current;
if (storages[index]) {
Expand All @@ -48,24 +45,6 @@ export function DefineConversation(props) {
});
};

const handleSubmit = e => {
e.preventDefault();
const errors = validateForm(formData);

if (Object.keys(errors).length) {
setFormData({
...formData,
errors,
});
return;
}

onSubmit({
...formData,
});
};

//disable the next button if the text has errors.
const getErrorMessage = text => {
if (typeof onGetErrorMessage === 'function') {
const result = onGetErrorMessage(text);
Expand All @@ -83,45 +62,41 @@ export function DefineConversation(props) {
}
};

// // update the path in the form and toggle the location picker.
const handleSubmit = e => {
e.preventDefault();
const errors = validateForm(formData);

if (Object.keys(errors).length) {
setFormData({
...formData,
errors,
});
return;
}

onSubmit({
...formData,
});
};

const updateLocation = path => {
updateForm('location')(null, path);
};

return (
<Fragment>
<form onSubmit={handleSubmit}>
<input type="submit" style={{ display: 'none' }} />
<Stack horizontal={enableLocationBrowse} gap="2rem" styles={wizardStyles.stackinput}>
<StackItem grow={0} styles={wizardStyles.halfstack}>
<TextField
label={formatMessage('Name')}
value={formData.name}
styles={name}
onChange={updateForm('name')}
errorMessage={formData.errors.name}
onGetErrorMessage={getErrorMessage}
data-testid="NewDialogName"
/>
</StackItem>
<StackItem grow={0} styles={wizardStyles.halfstack}>
<TextField
styles={description}
value={formData.description}
label={formatMessage('Description')}
multiline
resizable={false}
onChange={updateForm('description')}
/>
</StackItem>
</Stack>
{enableLocationBrowse && <LocationSelectContent onChange={updateLocation} />}

<DialogFooter>
<DefaultButton onClick={onDismiss} text={formatMessage('Cancel')} />
<PrimaryButton onClick={handleSubmit} text={formatMessage('Next')} disabled={disable} />
</DialogFooter>
</form>
</Fragment>
<form onSubmit={handleSubmit}>
<DialogModal
onGetErrorMessage={onGetErrorMessage}
getErrorMessage={getErrorMessage}
updateForm={updateForm}
formData={formData}
horizontal={true}
/>
<LocationSelectContent onChange={updateLocation} />
<DialogFooter>
<DefaultButton onClick={onDismiss} text={formatMessage('Cancel')} />
<PrimaryButton onClick={handleSubmit} text={formatMessage('Next')} disabled={disable} />
</DialogFooter>
</form>
);
}
40 changes: 40 additions & 0 deletions Composer/packages/client/src/CreationFlow/DialogModal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { Fragment } from 'react';
import formatMessage from 'format-message';
import { Stack, StackItem, TextField } from 'office-ui-fabric-react';

import { styles as wizardStyles } from '../StepWizard/styles';

import { name, description } from './styles';

export function DialogModal(props) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good to see that, you are towards the direction to make more pure component.

But the interface here needs a bit rework, for example,

think about what's your "DialogModal“ component is designed for, then looks at this props.

Multiple problems, what do you mean by form? What should in the form? if your form only holds one data field, it's probably should be called field?

then this "updateForm, getErrorMessage", if you look at the naming, it's not in the same level, i think you mean onChange, onError?

But you know, if you delegate onChange, there should not be an on Error, because onChange tells everything, if you want to design an onError, you want to put a validator here, right?

Let's talk about this tomorrow.

const { horizontal, formData, updateForm, getErrorMessage } = props;

return (
<Fragment>
<input type="submit" style={{ display: 'none' }} />
<Stack horizontal={horizontal} gap="2rem" styles={wizardStyles.stackinput}>
<StackItem grow={0} styles={wizardStyles.halfstack}>
<TextField
label={formatMessage('Name')}
value={formData.name}
styles={name}
onChange={updateForm('name')}
errorMessage={formData.errors.name}
onGetErrorMessage={getErrorMessage}
data-testid="NewDialogName"
/>
</StackItem>
<StackItem grow={0} styles={wizardStyles.halfstack}>
<TextField
styles={description}
value={formData.description}
label={formatMessage('Description')}
multiline
resizable={false}
onChange={updateForm('description')}
/>
</StackItem>
</Stack>
</Fragment>
);
}
9 changes: 2 additions & 7 deletions Composer/packages/client/src/CreationFlow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { toLower } from 'lodash';
import { CreationFlowStatus, DialogCreationCopy, Steps } from '../constants';

import { CreateOptions } from './CreateOptions/index';
import { DefineConversation } from './DefineConversation/index';
import DefineConversation from './DefineConversation/index';
import { OpenProject } from './OpenProject';
import { StoreContext } from './../store';
import { StepWizard } from './StepWizard/StepWizard';
Expand Down Expand Up @@ -128,12 +128,7 @@ export function CreationFlow(props) {
[Steps.DEFINE]: {
...DialogCreationCopy.DEFINE_CONVERSATION_OBJECTIVE,
children: (
<DefineConversation
onSubmit={handleSubmit}
onGetErrorMessage={getErrorMessage}
onDismiss={handleDismiss}
enableLocationBrowse={true}
/>
<DefineConversation onSubmit={handleSubmit} onGetErrorMessage={getErrorMessage} onDismiss={handleDismiss} />
),
},
};
Expand Down
89 changes: 80 additions & 9 deletions Composer/packages/client/src/pages/design/new-dialog-modal.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,91 @@
import React from 'react';
import React, { useState } from 'react';
import { DialogFooter, PrimaryButton, DefaultButton } from 'office-ui-fabric-react';
import formatMessage from 'format-message';

import { DialogCreationCopy } from '../../constants';
import { DefineConversation } from '../../CreationFlow/DefineConversation/index';
import { DialogModal } from '../../CreationFlow/DialogModal/index';
import { DialogWrapper } from '../../components/DialogWrapper/index';

const nameRegex = /^[a-zA-Z0-9-_.]+$/;

const validateForm = data => {
const errors = {};
const { name } = data;

if (!name || !nameRegex.test(name)) {
errors.name = formatMessage(
'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _'
);
}

return errors;
};

export default function NewDialogModal(props) {
const { isOpen, onDismiss, onSubmit, onGetErrorMessage } = props;
const [formData, setFormData] = useState({ errors: {} });
const [disable, setDisable] = useState(false);
const updateForm = field => (e, newValue) => {
setFormData({
...formData,
errors: {},
[field]: newValue,
});
};

const getErrorMessage = text => {
if (typeof onGetErrorMessage === 'function') {
const result = onGetErrorMessage(text);
if (result === '' && disable) {
setDisable(false);
}

if (result !== '' && !disable) {
setDisable(true);
}

return result;
} else {
return '';
}
};

const onClickCancel = () => {
setFormData({ errors: {} });
onDismiss();
};

const handleSubmit = e => {
e.preventDefault();
const errors = validateForm(formData);

if (Object.keys(errors).length) {
setFormData({
...formData,
errors,
});
return;
}

onSubmit({
...formData,
});
};
return (
<DialogWrapper isOpen={isOpen} onDismiss={onDismiss} {...DialogCreationCopy.DEFINE_CONVERSATION_OBJECTIVE}>
<DefineConversation
onSubmit={onSubmit}
onDismiss={onDismiss}
onGetErrorMessage={onGetErrorMessage}
enableLocationBrowse={false}
/>
<DialogWrapper isOpen={isOpen} onDismiss={onClickCancel} {...DialogCreationCopy.DEFINE_CONVERSATION_OBJECTIVE}>
<form onSubmit={handleSubmit}>
<DialogModal
onGetErrorMessage={onGetErrorMessage}
horizontal={false}
getErrorMessage={getErrorMessage}
updateForm={updateForm}
formData={formData}
/>
<DialogFooter>
<DefaultButton onClick={onClickCancel} text={formatMessage('Cancel')} />
<PrimaryButton onClick={handleSubmit} text={formatMessage('Next')} disabled={disable} />
</DialogFooter>
</form>
</DialogWrapper>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class AssetManager {
const folders = await this.templateStorage.readDir(path);
this.projectTemplates = [];
for (const name of folders) {
if (!templates[name]) continue;
const absPath = Path.join(path, name);
if ((await this.templateStorage.stat(absPath)).isDir) {
const base = { id: name, name: templates[name].name, description: templates[name].description };
Expand Down