Skip to content

Commit

Permalink
feat: add form block
Browse files Browse the repository at this point in the history
  • Loading branch information
nzambello committed Mar 11, 2021
1 parent 33b5ad6 commit 8857ec5
Show file tree
Hide file tree
Showing 13 changed files with 1,336 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* emailNotification actions.
* @module actions/emailNotification/emailNotification
*/

export const EMAIL_SEND_ACTION_FORM = 'EMAIL_SEND_ACTION_FORM';

/**
* Email send function
* @function sendActionForm
* @param {string} pat
* @param {string} block_id
* @param {Object} data
* @returns {Object} attachments
*/
export function sendActionForm(path = '', block_id, data, attachments) {
return {
type: EMAIL_SEND_ACTION_FORM,
request: {
op: 'post',
path: path + '/@send-action-form',
data: {
block_id,
data,
attachments,
},
},
};
}
121 changes: 121 additions & 0 deletions src/components/Edit.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React from 'react';
import EditBlock from './EditBlock';

import { Segment, Grid, Form, Button } from 'semantic-ui-react';
import {
withDNDContext,
SubblocksEdit,
SubblocksWrapper,
} from 'volto-subblocks';

import { SidebarPortal } from '@plone/volto/components';
import Sidebar from './Sidebar.jsx';

import { defineMessages } from 'react-intl';

const messages = defineMessages({
addField: {
id: 'Add field',
defaultMessage: 'Aggiungi un campo',
},
default_submit_label: {
id: 'form_default_submit_label',
defaultMessage: 'Invia',
},
warning: {
id: 'form_edit_warning',
defaultMessage: 'Attenzione!',
},
warning_from: {
id: 'form_edit_warning_from',
defaultMessage:
'Inserire un campo di tipo "E-mail mittente". Se non è presente, oppure è presente ma non viene compilato dall\'utente, l\'indirizzo del mittente della mail sarà quello configurato dalla sidebar di destra.',
},
});

/**
* Edit Form block class.
* @class Edit
* @extends Component
*/
class Edit extends SubblocksEdit {
componentDidMount() {
super.componentDidMount();

if (!this.props.data.default_from) {
this.props.onChangeBlock(this.props.block, {
...this.props.data,
default_from: 'noreply@plone.org',
lastChange: new Date().getTime(),
});
}
}

/**
* Render method.
* @method render
* @returns {string} Markup for the component.
*/

render() {
if (__SERVER__) {
return <div />;
}

return (
<div className="public-ui">
<Segment>
<SubblocksWrapper node={this.node}>
{this.state.subblocks.map((subblock, subindex) => (
<Form.Field key={subindex}>
<EditBlock
data={subblock}
index={subindex}
selected={this.isSubblockSelected(subindex)}
{...this.subblockProps}
openObjectBrowser={this.props.openObjectBrowser}
/>
</Form.Field>
))}

{this.props.selected && (
<Form.Field>
{this.renderAddBlockButton(
this.props.intl.formatMessage(messages.addField),
)}
</Form.Field>
)}

<Grid columns={1} padded="vertically">
<Grid.Row>
<Grid.Column textAlign="center">
<Button primary>
{this.props.data.submit_label ||
this.props.intl.formatMessage(
messages.default_submit_label,
)}
</Button>
</Grid.Column>
</Grid.Row>
</Grid>
</SubblocksWrapper>
</Segment>

<SidebarPortal selected={this.props.selected || false}>
<Sidebar
{...this.props}
data={this.props.data}
block={this.props.block}
onChangeBlock={this.props.onChangeBlock}
onChangeSubBlock={this.onChangeSubblocks}
selected={this.state.subIndexSelected}
setSelected={this.onSubblockChangeFocus}
openObjectBrowser={this.props.openObjectBrowser}
/>
</SidebarPortal>
</div>
);
}
}

export default React.memo(withDNDContext(Edit));
64 changes: 64 additions & 0 deletions src/components/EditBlock.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Edit text block.
* @module components/manage/Blocks/Title/Edit
*/

import React from 'react';
import { compose } from 'redux';

import { DNDSubblocks, SubblockEdit, Subblock } from 'volto-subblocks';

import Field from './Field';
import { getFieldName } from './utils';

/**
* Edit text block class.
* @class Edit
* @extends Component
*/
class EditBlock extends SubblockEdit {
/**
* Constructor
* @method constructor
* @param {Object} props Component properties
* @constructs WysiwygEditor
*/
constructor(props) {
super(props);
//default subblock values
if (!props.data.field_type) {
this.onChange({
field_type: 'text',
});
}
}

/**
* Render method.
* @method render
* @returns {string} Markup for the component.
*/
render() {
if (__SERVER__) {
return <div />;
}
const id = new Date().getTime();
return (
<Subblock subblock={this} className="subblock-edit">
<div key={this.props.data.index}>
<Field
{...this.props.data}
name={getFieldName(this.props.data.label)}
key={this.props.data.index}
isOnEdit={true}
id={id}
index={this.props.data.index}
onChange={() => {}}
/>
</div>
</Subblock>
);
}
}

export default React.memo(compose(...DNDSubblocks)(EditBlock));
179 changes: 179 additions & 0 deletions src/components/Field.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl, defineMessages } from 'react-intl';
import TextWidget from '@plone/volto/components/manage/Widgets/TextWidget';
import TextareaWidget from '@plone/volto/components/manage/Widgets/TextareaWidget';
import SelectWidget from '@plone/volto/components/manage/Widgets/SelectWidget';
import EmailWidget from '@plone/volto/components/manage/Widgets/EmailWidget';
import CheckboxWidget from '@plone/volto/components/manage/Widgets/CheckboxWidget';
import { DatetimeWidget } from '@plone/volto/components';

import RadioWidget from './Widget/RadioWidget';
import FileWidget from './Widget/FileWidget';

const messages = defineMessages({
select_a_value: {
id: 'form_select_a_value',
defaultMessage: 'Select a value',
},
});

/**
* Field class.
* @class View
* @extends Component
*/
const Field = ({
label,
description,
name,
field_type,
required,
input_values,
value,
onChange,
isOnEdit,
valid,
}) => {
const intl = useIntl();

const getLabel = () => {
return required ? label + ' *' : label;
};

const isInvalid = () => {
return !isOnEdit && !valid;
};

return (
<div className="field">
{field_type === 'text' && (
<TextWidget
id={name}
name={name}
title={getLabel()}
description={description}
required={required}
onChange={onChange}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
{field_type === 'textarea' && (
<TextareaWidget
id={name}
name={name}
title={getLabel()}
description={description}
required={required}
onChange={onChange}
rows={10}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
{field_type === 'select' && (
<SelectWidget
id={name}
name={name}
title={getLabel()}
description={description}
getVocabulary={() => {}}
getVocabularyTokenTitle={() => {}}
choices={[
...(input_values?.map((v) => ({ value: v, label: v })) ?? []),
]}
onChange={onChange}
placeholder={intl.formatMessage(messages.select_a_value)}
aria-label={intl.formatMessage(messages.select_a_value)}
classNamePrefix="react-select"
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
{field_type === 'radio' && (
<RadioWidget
id={name}
title={getLabel()}
description={description}
required={required}
onChange={onChange}
valueList={[
...(input_values?.map((v) => ({ value: v, label: v })) ?? []),
]}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
{field_type === 'checkbox' && (
<CheckboxWidget
id={name}
name={name}
title={getLabel()}
description={description}
required={required}
onChange={onChange}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
{field_type === 'date' && (
<DatetimeWidget
id={name}
name={name}
title={getLabel()}
description={description}
dateOnly={true}
noPastDates={false}
onChange={onChange}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
{field_type === 'attachment' && (
<FileWidget
id={name}
name={name}
label={getLabel()}
type="file"
required={required}
infoText={description}
invalid={isInvalid().toString()}
onChange={onChange}
onEdit={isOnEdit}
value={value}
/>
)}
{(field_type === 'from' || field_type === 'email') && (
<EmailWidget
id={name}
name={name}
title={getLabel()}
description={description}
required={required}
onChange={onChange}
invalid={isInvalid().toString()}
{...(isInvalid() ? { className: 'is-invalid' } : {})}
/>
)}
</div>
);
};

/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
Field.propTypes = {
label: PropTypes.string,
name: PropTypes.string,
description: PropTypes.string,
required: PropTypes.bool,
field_type: PropTypes.string,
input_values: PropTypes.any,
value: PropTypes.any,
onChange: PropTypes.func,
};

export default Field;
Loading

0 comments on commit 8857ec5

Please sign in to comment.