Compare commits

...

42 Commits

Author SHA1 Message Date
56782e2f10 Release 3.46.2 2020-03-03 09:14:09 +01:00
698c523d25 Remove React.FC
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
dc280fd38c Ignore PropTypes ESLint warnings
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
e951591d6f Use Typescript instead of InferProps
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
34409432e8 Fix ESlint for Typescript
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
0ceac8921f Add Typescript linting to qa:lint-javascript command
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
20909f0912 Generate PropTypes from Typescript definitions
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
a669beed07 Add ESLint rule for Typescript files
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
9de1481efe Add First .ts file
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
33948892fa Add possibility to omit extensions
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
83f3729e37 Refactor APIErrorsNotice to Typescript
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
25aec60bc9 Refactor Notice component to Typescript
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
cbe2fc64bb Install and configure Typescript
[MAILPOET-2658]
2020-03-02 20:55:24 +00:00
9547839aa9 Remove unused component
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
f5fc03ee1d Fix preview
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
61251f6e34 Fix styling form bellow post
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
ec8adcae73 Move form wrapping to widget
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
691a8a45f3 Render font size
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
ce127eadc1 Render styles for the form
[MAILPOET-2600]

Signed-off-by: Pavel Dohnal <pavel@mailpoet.com>
2020-03-02 20:52:38 +00:00
b49ce7d4e4 Improve styling of styles settings panel
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
d83975dc64 Use font picker from components
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
57b0747b6e Remove toggle that has been removed from specs
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
24c87ad19e Add font size title
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
2c7b8a7d25 Use font size from settings
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
07bbdc59f6 Add font size control
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
3f74b74ff0 Rename component
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
713908b6c3 Display texts using selected colour
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
fbc650e331 Add component for font colour
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
eef74fa2cc Display background colour
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
317bed32b1 Add component for background colour
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
8fa61ca883 Update Guteneberg packages
They added some minor fixes before release of wp5.4
[MAILPOET-2609]
2020-03-02 20:52:38 +00:00
a1df6acdcc Add Form Styles Panel
[MAILPOET-2600]
2020-03-02 20:52:38 +00:00
ac557d692e Fix throttling integration test
[MAILPOET-2723]
2020-03-02 12:49:26 +00:00
7b2eef18ad Fix font color when deleting editor block
[MAILPOET-2723]
2020-03-02 12:49:26 +00:00
c9c1b1fa92 Remove input because it would never apply
Renderer adds the class directly to the input element
and the css would never apply.

[MAILPOET-2601]
2020-02-27 18:49:59 +00:00
927d9cf87e Add classes to inputs
[MAILPOET-2601]
2020-02-27 18:49:59 +00:00
daefe143a0 Add mailpoet_paragraph class to all blocks
[MAILPOET-2601]
2020-02-27 18:49:59 +00:00
21926ea3e0 Add mailpoet_form class to form
[MAILPOET-2601]
2020-02-27 18:49:59 +00:00
9e5acecbc0 Fix useNotices implementation 2020-02-26 19:45:43 +00:00
c921bc6f71 Use useMemo and useCallback to memoize context values 2020-02-26 19:45:43 +00:00
2f2a00cbbe Remove MailPoet.Notice 2020-02-26 19:45:43 +00:00
493d6caffc Release 3.46.1 2020-02-25 13:17:45 +01:00
90 changed files with 1214 additions and 363 deletions

View File

@ -1,9 +1,11 @@
{
"presets": [
"@babel/preset-typescript",
"@babel/preset-react",
"@babel/preset-env"
],
"plugins": [
"babel-plugin-typescript-to-proptypes",
"@babel/plugin-proposal-class-properties",
[
"@babel/plugin-transform-runtime", {

137
.eslintrc.ts.json Normal file
View File

@ -0,0 +1,137 @@
{
"extends": [
"airbnb",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"env": {
"amd": true,
"browser": true,
"mocha": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"tsconfigRootDir": ".",
"project": ["./tsconfig.json"],
"ecmaVersion": 6,
"ecmaFeatures": {
"jsx": true
}
},
"plugins": [
"react-hooks",
"no-only-tests",
"@typescript-eslint"
],
"settings": {
"import/resolver": "webpack"
},
"rules": {
// Typrescript Rules
"@typescript-eslint/adjacent-overload-signatures": "off",
"@typescript-eslint/array-type": "off",
"@typescript-eslint/await-thenable": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/consistent-type-assertions": "off",
"@typescript-eslint/consistent-type-definitions": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/member-delimiter-style": "off",
"@typescript-eslint/member-ordering": "off",
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-extra-non-null-assertion": "off",
"@typescript-eslint/no-extraneous-class": "off",
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-for-in-array": "off",
"@typescript-eslint/no-implied-eval": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-misused-new": "off",
"@typescript-eslint/no-misused-promises": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-non-null-asserted-optional-chain": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-throw-literal": "off",
"@typescript-eslint/no-type-alias": "off",
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "off",
"@typescript-eslint/no-unnecessary-condition": "off",
"@typescript-eslint/no-unnecessary-qualifier": "off",
"@typescript-eslint/no-unnecessary-type-arguments": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/no-unused-vars-experimental": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/prefer-as-const": "off",
"@typescript-eslint/prefer-for-of": "off",
"@typescript-eslint/prefer-function-type": "off",
"@typescript-eslint/prefer-includes": "off",
"@typescript-eslint/prefer-namespace-keyword": "off",
"@typescript-eslint/prefer-nullish-coalescing": "off",
"@typescript-eslint/prefer-optional-chain": "off",
"@typescript-eslint/prefer-readonly": "off",
"@typescript-eslint/prefer-regexp-exec": "off",
"@typescript-eslint/prefer-string-starts-ends-with": "off",
"@typescript-eslint/promise-function-async": "off",
"@typescript-eslint/require-array-sort-compare": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/switch-exhaustiveness-check": "off",
"@typescript-eslint/triple-slash-reference": "off",
"@typescript-eslint/type-annotation-spacing": "off",
"@typescript-eslint/typedef": "off",
"@typescript-eslint/unbound-method": "off",
"@typescript-eslint/unified-signatures": "off",
// ESlint recommended rules adjusted for Typescript
"@typescript-eslint/brace-style": "off",
"@typescript-eslint/comma-spacing": "off",
"@typescript-eslint/default-param-last": "off",
"@typescript-eslint/func-call-spacing": "off",
"@typescript-eslint/indent": "off",
"@typescript-eslint/no-array-constructor": "off",
"@typescript-eslint/no-dupe-class-members": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-extra-parens": "off",
"@typescript-eslint/no-extra-semi": "off",
"@typescript-eslint/no-magic-numbers": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-useless-constructor": "off",
"@typescript-eslint/quotes": "off",
"@typescript-eslint/require-await": "off",
"@typescript-eslint/return-await": "off",
"@typescript-eslint/semi": "off",
"@typescript-eslint/space-before-function-paren": "off",
"@typescript-eslint/camelcase": "off",
// PropTypes
"react/prop-types": 0,
// Hooks
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
// Exceptions
"react/jsx-filename-extension": 0,
"arrow-parens": ["error", "always"],
"comma-dangle": ["error", "always-multiline"],
"no-only-tests/no-only-tests": 2,
"no-script-url": 0,
"import/extensions": 0, // we wouldn't be able to import jQuery without this line
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
"prefer-destructuring": 0, // that would be too many changes to fix this one
"jsx-a11y/label-has-for": [2, {
"required": {"some": ["nesting", "id"]} // some of our labels are hidden and we cannot nest those
}],
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
"jsx-a11y/label-has-associated-control": [ 2, {
"either": "either" // control has to be either nested or associated via htmlFor
}]
}
}

View File

@ -363,7 +363,7 @@ class RoboFile extends \Robo\Tasks {
}
public function qaLintJavascript() {
return $this->_exec('npm run lint');
return $this->_exec('npm run check-types && npm run lint');
}
public function qaLintCss() {
@ -445,11 +445,22 @@ class RoboFile extends \Robo\Tasks {
)
->run();
}
if (substr($filePath, -4) === '.tsx' || substr($filePath, -3) === '.ts') {
// fix ESLint using TS rules
return $this->collectionBuilder()
->taskExec(
'npx eslint -c .eslintrc.ts.json ' .
'--max-warnings 0 ' .
'--fix ' .
$filePath
)
->run();
}
if (substr($filePath, -8) === '.spec.js') {
// fix ESLint using tests rules
return $this->collectionBuilder()
->taskExec(
'npx eslint -c .eslintrc.tests.json ' .
'npx eslint -c .eslintrc.tests_newsletter_editor.json ' .
'--max-warnings 0 ' .
'--fix ' .
$filePath

View File

@ -86,6 +86,20 @@ $gutenberg-control-border-color-focus: #007cba;
}
}
.mailpoet-styles-settings {
display: grid;
grid-auto-flow: row;
grid-row-gap: 30px;
}
.mailpoet-styles-settings-heading {
font-size: 15px;
.component-color-indicator {
vertical-align: text-bottom;
}
}
.CodeMirror {
border: 1px solid #eee;
}

View File

@ -174,6 +174,24 @@ $column-icon-size-with-padding: 27px;
@include animation-fade-in-and-scale-horizontally();
}
.mailpoet_delete_block_confirm {
color: $warning-text-color;
&:hover {
color: $warning-text-color;
text-decoration: underline;
}
}
.mailpoet_delete_block_cancel {
color: $warning-alternate-text-color;
&:hover {
color: $warning-alternate-text-color;
text-decoration: underline;
}
}
}
.mailpoet_delete_block_activated {
@ -199,21 +217,3 @@ $column-icon-size-with-padding: 27px;
margin-left: 3px;
}
}
.mailpoet_delete_block_confirm {
color: $warning-text-color;
&:hover {
color: $warning-text-color;
text-decoration: underline;
}
}
.mailpoet_delete_block_cancel {
color: $warning-alternate-text-color;
&:hover {
color: $warning-alternate-text-color;
text-decoration: underline;
}
}

View File

@ -25,7 +25,7 @@ export const fromUrl = (url) => new Promise((resolve, reject) => {
const iframe = document.createElement('iframe');
const protocol = document.location.href.startsWith('https://') ? 'https:' : 'http:';
iframe.src = protocol + url.replace(/^https?:/, '');
iframe.style.opacity = 0;
iframe.style.opacity = '0';
iframe.scrolling = 'no';
iframe.onload = async () => {
const container = iframe.contentDocument.documentElement;
@ -44,7 +44,7 @@ export const fromUrl = (url) => new Promise((resolve, reject) => {
reject(MailPoet.I18n.t('errorWhileTakingScreenshot'));
};
iframe.onerror = onError;
iframe.onError = onError;
(iframe as any).onError = onError;
iframe.className = 'mailpoet_template_iframe';
try {
document.body.appendChild(iframe);
@ -65,7 +65,7 @@ export const fromNewsletter = (data) => new Promise((resolve, reject) => {
json.body = JSON.stringify(json.body);
}
MailPoet.Ajax.post({
api_version: window.mailpoet_api_version,
api_version: (window as any).mailpoet_api_version,
endpoint: 'newsletters',
action: 'showPreview',
data: json,

View File

@ -1,5 +0,0 @@
export default function getFeaturesContext(data) {
const flags = data.mailpoet_feature_flags;
const isSupported = (feature) => flags[feature] || false;
return { isSupported };
}

View File

@ -1,5 +0,0 @@
export default function getSegmentsContext(data) {
return {
all: data.mailpoetSegments,
};
}

View File

@ -1,5 +0,0 @@
export default function getUsersContext(data) {
return {
isNewUser: data.mailpoet_is_new_user,
};
}

View File

@ -1,7 +1,7 @@
import React from 'react';
import getFeaturesContext from './getFeaturesContext.jsx';
import getSegmentsContext from './getSegmentsContext.jsx';
import getUsersContext from './getUsersContext.jsx';
import useFeaturesContext from './useFeaturesContext.jsx';
import useSegmentsContext from './useSegmentsContext.jsx';
import useUsersContext from './useUsersContext.jsx';
import useNotices from './useNotices.jsx';
/**
@ -10,9 +10,9 @@ import useNotices from './useNotices.jsx';
* some React hooks to build some parts of the context.
*/
export function useGlobalContextValue(data) {
const features = getFeaturesContext(data);
const segments = getSegmentsContext(data);
const users = getUsersContext(data);
const features = useFeaturesContext(data);
const segments = useSegmentsContext(data);
const users = useUsersContext(data);
const notices = useNotices();
return {
features, segments, users, notices,

View File

@ -0,0 +1,9 @@
import React from 'react';
export default function useFeaturesContext(data) {
return React.useMemo(() => {
const flags = data.mailpoet_feature_flags;
const isSupported = (feature) => flags[feature] || false;
return { isSupported };
}, [data]);
}

View File

@ -1,28 +1,43 @@
import React from 'react';
export default () => {
const [items, setItems] = React.useState([]);
const [nextId, setNextId] = React.useState(1);
const [state, setState] = React.useState({
items: [],
nextId: 1,
});
const getNextId = () => {
setNextId((x) => x + 1);
return nextId;
};
const add = React.useCallback((item) => {
setState(({ items, nextId }) => ({
items: [...items, { ...item, id: item.id || nextId }],
nextId: item.id ? nextId : nextId + 1,
}));
}, [setState]);
const add = (item) => {
setItems((xs) => [...xs, { ...item, id: item.id || getNextId() }]);
};
const remove = React.useCallback((id) => {
setState(({ items, nextId }) => ({
items: items.filter((x) => x.id !== id),
nextId,
}));
}, [setState]);
const remove = (id) => {
setItems((xs) => xs.filter((x) => x.id !== id));
};
const success = (content, props = {}) => add({ ...props, type: 'success', children: content });
const info = (content, props = {}) => add({ ...props, type: 'info', children: content });
const warning = (content, props = {}) => add({ ...props, type: 'warning', children: content });
const error = (content, props = {}) => add({ ...props, type: 'error', children: content });
const success = React.useCallback(
(content, props = {}) => add({ ...props, type: 'success', children: content }),
[add]
);
const info = React.useCallback(
(content, props = {}) => add({ ...props, type: 'info', children: content }),
[add]
);
const warning = React.useCallback(
(content, props = {}) => add({ ...props, type: 'warning', children: content }),
[add]
);
const error = React.useCallback(
(content, props = {}) => add({ ...props, type: 'error', children: content }),
[add]
);
return {
items, success, info, warning, error, remove,
items: state.items, success, info, warning, error, remove,
};
};

View File

@ -0,0 +1,7 @@
import React from 'react';
export default function useSegmentsContext(data) {
return React.useMemo(() => ({
all: data.mailpoetSegments,
}), [data]);
}

View File

@ -0,0 +1,7 @@
import React from 'react';
export default function useUsersContext(data) {
return React.useMemo(() => ({
isNewUser: data.mailpoet_is_new_user,
}), [data]);
}

View File

@ -7,6 +7,7 @@ import Notices from 'notices/notices.jsx';
const ExperimentalFeatures = () => {
const [flags, setFlags] = useState(null);
const contextValue = useGlobalContextValue(window);
const showError = contextValue.notices.error;
useEffect(() => {
MailPoet.Ajax.post({
@ -18,13 +19,13 @@ const ExperimentalFeatures = () => {
setFlags(flagsMap);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
showError(
<>{response.errors.map((error) => <p>{error.message}</p>)}</>,
{ scroll: true }
);
}
});
}, []);
}, [showError]);
function handleChange(event) {
const name = event.target.name;
@ -41,11 +42,12 @@ const ExperimentalFeatures = () => {
const flag = flags[name];
flag.value = value;
setFlags({ ...flags, [name]: flag });
MailPoet.Notice.success(`Feature '${name}' was ${value ? 'enabled' : 'disabled'}.`);
const message = `Feature '${name}' was ${value ? 'enabled' : 'disabled'}.`;
contextValue.notices.success(<p>{message}</p>);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
showError(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}

View File

@ -1,6 +1,7 @@
import React from 'react';
import moment from 'moment';
import PropTypes from 'prop-types';
import classnames from 'classnames';
function FormFieldDateYear(props) {
const yearsRange = 100;
@ -28,6 +29,7 @@ function FormFieldDateYear(props) {
name={`${props.name}[year]`}
value={props.year}
onChange={props.onValueChange}
className={classnames({ mailpoet_date_year: props.addDefaultClasses })}
>
{ years }
</select>
@ -42,6 +44,7 @@ FormFieldDateYear.propTypes = {
PropTypes.string,
PropTypes.number,
]).isRequired,
addDefaultClasses: PropTypes.bool.isRequired,
};
function FormFieldDateMonth(props) {
@ -68,6 +71,7 @@ function FormFieldDateMonth(props) {
name={`${props.name}[month]`}
value={props.month}
onChange={props.onValueChange}
className={classnames({ mailpoet_date_month: props.addDefaultClasses })}
>
{ months }
</select>
@ -83,6 +87,7 @@ FormFieldDateMonth.propTypes = {
PropTypes.number,
]).isRequired,
monthNames: PropTypes.arrayOf(PropTypes.string).isRequired,
addDefaultClasses: PropTypes.bool.isRequired,
};
function FormFieldDateDay(props) {
@ -110,6 +115,7 @@ function FormFieldDateDay(props) {
name={`${props.name}[day]`}
value={props.day}
onChange={props.onValueChange}
className={classnames({ mailpoet_date_day: props.addDefaultClasses })}
>
{ days }
</select>
@ -124,6 +130,7 @@ FormFieldDateDay.propTypes = {
PropTypes.string,
PropTypes.number,
]).isRequired,
addDefaultClasses: PropTypes.bool.isRequired,
};
class FormFieldDate extends React.Component {
@ -253,6 +260,7 @@ class FormFieldDate extends React.Component {
onValueChange={this.onValueChange}
key="year"
name={this.props.field.name}
addDefaultClasses={this.props.addDefaultClasses}
year={this.state.year}
placeholder={this.props.field.year_placeholder}
/>
@ -264,6 +272,7 @@ class FormFieldDate extends React.Component {
onValueChange={this.onValueChange}
key="month"
name={this.props.field.name}
addDefaultClasses={this.props.addDefaultClasses}
month={this.state.month}
monthNames={monthNames}
placeholder={this.props.field.month_placeholder}
@ -276,6 +285,7 @@ class FormFieldDate extends React.Component {
onValueChange={this.onValueChange}
key="day"
name={this.props.field.name}
addDefaultClasses={this.props.addDefaultClasses}
day={this.state.day}
placeholder={this.props.field.day_placeholder}
/>
@ -304,6 +314,11 @@ FormFieldDate.propTypes = {
params: PropTypes.object, // eslint-disable-line react/forbid-prop-types
}).isRequired,
onValueChange: PropTypes.func.isRequired,
addDefaultClasses: PropTypes.bool,
};
FormFieldDate.defaultProps = {
addDefaultClasses: false,
};
export default FormFieldDate;

View File

@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { useDispatch, useSelect } from '@wordpress/data';
import ParagraphEdit from '../paragraph_edit.jsx';
import CustomFieldSettings from './custom_field_settings.jsx';
import mapCustomFieldFormData from '../map_custom_field_form_data.jsx';
@ -109,7 +110,7 @@ const CustomCheckboxEdit = ({ attributes, setAttributes, clientId }) => {
checkboxLabel += ' *';
}
return (
<>
<ParagraphEdit>
{inspectorControls}
<span className="mailpoet_checkbox_label">{getLabel()}</span>
<div>
@ -118,11 +119,12 @@ const CustomCheckboxEdit = ({ attributes, setAttributes, clientId }) => {
type="checkbox"
disabled
checked={isChecked()}
className="mailpoet_checkbox"
/>
{checkboxLabel}
</label>
</div>
</>
</ParagraphEdit>
);
};

View File

@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { useDispatch, useSelect } from '@wordpress/data';
import ParagraphEdit from '../paragraph_edit.jsx';
import CustomFieldSettings from './custom_field_settings.jsx';
import FormFieldDate from '../../../form/fields/date.jsx';
import formatLabel from '../label_formatter.jsx';
@ -80,28 +81,31 @@ const CustomDateEdit = ({ attributes, setAttributes, clientId }) => {
);
return (
<div className="mailpoet_custom_date">
{inspectorControls}
<label className="mailpoet_date_label" data-automation-id="editor_custom_date_label" htmlFor={clientId}>
{formatLabel(attributes)}
</label>
<FormFieldDate
field={{
name: clientId,
day_placeholder: MailPoet.I18n.t('customFieldDay'),
month_placeholder: MailPoet.I18n.t('customFieldMonth'),
year_placeholder: MailPoet.I18n.t('customFieldYear'),
params: {
date_type: attributes.dateType,
date_format: attributes.dateFormat,
},
}}
item={{
[clientId]: attributes.defaultToday ? moment().format('YYYY-MM-DD') : '',
}}
onValueChange={() => {}}
/>
</div>
<ParagraphEdit>
<div className="mailpoet_custom_date">
{inspectorControls}
<label className="mailpoet_date_label" data-automation-id="editor_custom_date_label" htmlFor={clientId}>
{formatLabel(attributes)}
</label>
<FormFieldDate
field={{
name: clientId,
day_placeholder: MailPoet.I18n.t('customFieldDay'),
month_placeholder: MailPoet.I18n.t('customFieldMonth'),
year_placeholder: MailPoet.I18n.t('customFieldYear'),
params: {
date_type: attributes.dateType,
date_format: attributes.dateFormat,
},
}}
item={{
[clientId]: attributes.defaultToday ? moment().format('YYYY-MM-DD') : '',
}}
addDefaultClasses
onValueChange={() => {}}
/>
</div>
</ParagraphEdit>
);
};

View File

@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { useDispatch, useSelect } from '@wordpress/data';
import ParagraphEdit from '../paragraph_edit.jsx';
import CustomFieldSettings from './custom_field_settings.jsx';
import formatLabel from '../label_formatter.jsx';
import mapCustomFieldFormData from '../map_custom_field_form_data.jsx';
@ -80,7 +81,7 @@ const CustomRadioEdit = ({ attributes, setAttributes, clientId }) => {
};
return (
<>
<ParagraphEdit>
{inspectorControls}
<span className="mailpoet_radio_label">{getLabel()}</span>
{Array.isArray(attributes.values) && attributes.values.map((value) => (
@ -90,12 +91,13 @@ const CustomRadioEdit = ({ attributes, setAttributes, clientId }) => {
type="radio"
disabled
checked={value.isChecked || false}
className="mailpoet_radio"
/>
{value.name}
</label>
</div>
))}
</>
</ParagraphEdit>
);
};

View File

@ -9,8 +9,9 @@ import { InspectorControls } from '@wordpress/block-editor';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { useDispatch, useSelect } from '@wordpress/data';
import formatLabel from '../label_formatter.jsx';
import ParagraphEdit from '../paragraph_edit.jsx';
import formatLabel from '../label_formatter.jsx';
import CustomFieldSettings from '../custom_radio/custom_field_settings.jsx';
import mapCustomFieldFormData from '../map_custom_field_form_data.jsx';
@ -105,7 +106,7 @@ const CustomSelectEdit = ({ attributes, setAttributes, clientId }) => {
};
return (
<>
<ParagraphEdit>
{inspectorControls}
<div className="mailpoet_custom_select" data-automation-id="custom_select_block">
{!attributes.labelWithinInput ? (
@ -115,7 +116,7 @@ const CustomSelectEdit = ({ attributes, setAttributes, clientId }) => {
) : null}
{getInput()}
</div>
</>
</ParagraphEdit>
);
};

View File

@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { useDispatch, useSelect } from '@wordpress/data';
import ParagraphEdit from '../paragraph_edit.jsx';
import CustomFieldSettings from './custom_field_settings.jsx';
import formatLabel from '../label_formatter.jsx';
import mapCustomFieldFormData from '../map_custom_field_form_data.jsx';
@ -92,7 +93,7 @@ const CustomTextEdit = ({ attributes, setAttributes, clientId }) => {
);
return (
<>
<ParagraphEdit>
{inspectorControls}
{!attributes.labelWithinInput ? (
<label className="mailpoet_text_label" data-automation-id="editor_custom_text_label" htmlFor={clientId}>
@ -100,7 +101,7 @@ const CustomTextEdit = ({ attributes, setAttributes, clientId }) => {
</label>
) : null}
{getTextInput(attributes.labelWithinInput ? formatLabel(attributes) : '')}
</>
</ParagraphEdit>
);
};

View File

@ -10,6 +10,7 @@ import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { useDispatch, useSelect } from '@wordpress/data';
import ParagraphEdit from '../paragraph_edit.jsx';
import CustomFieldSettings from '../custom_text/custom_field_settings.jsx';
import formatLabel from '../label_formatter.jsx';
import mapCustomFieldFormData from '../map_custom_field_form_data.jsx';
@ -104,7 +105,7 @@ const CustomTextAreaEdit = ({ attributes, setAttributes, clientId }) => {
const getTextArea = (placeholder) => (
<textarea
id="custom_text"
className="mailpoet_text"
className="mailpoet_textarea"
name="custom_text"
disabled
data-automation-id="editor_custom_text_input"
@ -114,7 +115,7 @@ const CustomTextAreaEdit = ({ attributes, setAttributes, clientId }) => {
);
return (
<>
<ParagraphEdit>
{inspectorControls}
{attributes.labelWithinInput ? (getTextArea(formatLabel(attributes))
) : (
@ -125,7 +126,7 @@ const CustomTextAreaEdit = ({ attributes, setAttributes, clientId }) => {
{getTextArea('')}
</>
)}
</>
</ParagraphEdit>
);
};

View File

@ -3,5 +3,11 @@ import {
HorizontalRule,
} from '@wordpress/components';
const DividerEdit = () => (<HorizontalRule />);
import ParagraphEdit from '../paragraph_edit.jsx';
const DividerEdit = () => (
<ParagraphEdit>
<HorizontalRule className="mailpoet_divider" />
</ParagraphEdit>
);
export default DividerEdit;

View File

@ -9,6 +9,8 @@ import { InspectorControls } from '@wordpress/block-editor';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ParagraphEdit from '../paragraph_edit.jsx';
const EmailEdit = ({ attributes, setAttributes }) => {
const inspectorControls = (
<InspectorControls>
@ -44,7 +46,7 @@ const EmailEdit = ({ attributes, setAttributes }) => {
);
return (
<>
<ParagraphEdit>
{inspectorControls}
{!attributes.labelWithinInput ? (
<label className="mailpoet_text_label" data-automation-id="editor_email_label" htmlFor="email">
@ -52,7 +54,7 @@ const EmailEdit = ({ attributes, setAttributes }) => {
</label>
) : null}
{getTextInput(attributes.labelWithinInput ? `${attributes.label} *` : '')}
</>
</ParagraphEdit>
);
};

View File

@ -9,6 +9,7 @@ import { InspectorControls } from '@wordpress/block-editor';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ParagraphEdit from '../paragraph_edit.jsx';
import formatLabel from '../label_formatter.jsx';
const FirstNameEdit = ({ attributes, setAttributes }) => {
@ -51,7 +52,7 @@ const FirstNameEdit = ({ attributes, setAttributes }) => {
);
return (
<>
<ParagraphEdit>
{inspectorControls}
{!attributes.labelWithinInput ? (
<label className="mailpoet_text_label" data-automation-id="editor_first_name_label" htmlFor="first_name">
@ -59,7 +60,7 @@ const FirstNameEdit = ({ attributes, setAttributes }) => {
</label>
) : null}
{getTextInput(attributes.labelWithinInput ? formatLabel(attributes) : '')}
</>
</ParagraphEdit>
);
};

View File

@ -10,8 +10,22 @@ import { InspectorControls } from '@wordpress/block-editor';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import { debounce } from 'lodash';
import { useSelect } from '@wordpress/data';
import ParagraphEdit from '../paragraph_edit.jsx';
const CustomHtmlEdit = ({ attributes, setAttributes }) => {
const { fontColor, fontSize } = useSelect(
(select) => {
const settings = select('mailpoet-form-editor').getFormSettings();
return {
backgroundColor: settings.backgroundColor,
fontColor: settings.fontColor,
fontSize: settings.fontSize,
};
},
[]
);
const [renderedContent, setRenderedContent] = useState(attributes.content);
const setRenderedContentDebounced = useCallback(debounce((content) => {
setRenderedContent(content);
@ -44,14 +58,16 @@ const CustomHtmlEdit = ({ attributes, setAttributes }) => {
</InspectorControls>
);
const styles = attributes.nl2br ? ['body { white-space: pre-line; }'] : [];
if (fontColor) styles.push(` body {color: ${fontColor};}`);
if (fontSize) styles.push(` body {font-size: ${fontSize}px }`);
const key = `${renderedContent}_${styles}`;
return (
<>
<ParagraphEdit>
{inspectorControls}
<div>
<SandBox html={renderedContent} styles={styles} key={key} />
</div>
</>
</ParagraphEdit>
);
};

View File

@ -9,6 +9,7 @@ import { InspectorControls } from '@wordpress/block-editor';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ParagraphEdit from '../paragraph_edit.jsx';
import formatLabel from '../label_formatter.jsx';
const LastNameEdit = ({ attributes, setAttributes }) => {
@ -51,7 +52,7 @@ const LastNameEdit = ({ attributes, setAttributes }) => {
);
return (
<>
<ParagraphEdit>
{inspectorControls}
{!attributes.labelWithinInput ? (
<label className="mailpoet_text_label" data-automation-id="editor_last_name_label" htmlFor="last_name">
@ -59,7 +60,7 @@ const LastNameEdit = ({ attributes, setAttributes }) => {
</label>
) : null}
{getTextInput(attributes.labelWithinInput ? formatLabel(attributes) : '')}
</>
</ParagraphEdit>
);
};

View File

@ -0,0 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
const ParagraphEdit = ({ children }) => (
<div className="mailpoet_paragraph">
{children}
</div>
);
ParagraphEdit.propTypes = {
children: PropTypes.node.isRequired,
};
export default ParagraphEdit;

View File

@ -1,9 +1,8 @@
import React from 'react';
import {
CheckboxControl,
} from '@wordpress/components';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ParagraphEdit from '../paragraph_edit.jsx';
import Settings from './settings.jsx';
const SegmentSelectEdit = ({ attributes, setAttributes }) => {
@ -12,17 +11,21 @@ const SegmentSelectEdit = ({ attributes, setAttributes }) => {
return (<p className="mailpoet_error">{MailPoet.I18n.t('blockSegmentSelectNoLists')}</p>);
}
return attributes.values.map((value) => (
<CheckboxControl
label={value.name}
checked={!!value.isChecked}
disabled
key={value.id}
/>
<label key={value.id} className="mailpoet_checkbox_label">
<input
type="checkbox"
disabled
key={value.id}
checked={!!value.isChecked}
className="mailpoet_checkbox"
/>
{value.name}
</label>
));
};
return (
<>
<ParagraphEdit>
<Settings
label={attributes.label}
onLabelChanged={(label) => (setAttributes({ label }))}
@ -39,7 +42,7 @@ const SegmentSelectEdit = ({ attributes, setAttributes }) => {
{attributes.label}
</span>
{renderValues()}
</>
</ParagraphEdit>
);
};

View File

@ -8,6 +8,8 @@ import { InspectorControls } from '@wordpress/block-editor';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
import ParagraphEdit from '../paragraph_edit.jsx';
const SubmitEdit = ({ attributes, setAttributes }) => {
const inspectorControls = (
<InspectorControls>
@ -26,17 +28,15 @@ const SubmitEdit = ({ attributes, setAttributes }) => {
);
return (
<>
<ParagraphEdit>
{ inspectorControls }
<div className="mailpoet_submit">
<input
className="button"
type="submit"
value={attributes.label}
data-automation-id="editor_submit_input"
/>
</div>
</>
<input
className="button mailpoet_submit"
type="submit"
value={attributes.label}
data-automation-id="editor_submit_input"
/>
</ParagraphEdit>
);
};

View File

@ -17,6 +17,7 @@ import Notices from './notices.jsx';
import UnsavedChangesNotice from './unsaved_changes_notice.jsx';
import FormStyles from './form_styles.jsx';
import Preview from './preview.jsx';
import FormStylingBackground from './form_styling_background.jsx';
// Editor settings - see @wordpress/block-editor/src/store/defaults.js
const editorSettings = {
@ -67,12 +68,16 @@ export default () => {
<BlockSelectionClearer className="edit-post-visual-editor editor-styles-wrapper">
<BlockEditorKeyboardShortcuts />
<BlockEditorKeyboardShortcuts.Register />
<WritingFlow>
<ObserveTyping>
<FormTitle />
<BlockList />
</ObserveTyping>
</WritingFlow>
<div className="mailpoet_form">
<FormStylingBackground>
<WritingFlow>
<ObserveTyping>
<FormTitle />
<BlockList />
</ObserveTyping>
</WritingFlow>
</FormStylingBackground>
</div>
</BlockSelectionClearer>
</div>
<div className="block-editor-editor-skeleton__sidebar">

View File

@ -3,6 +3,7 @@ import React from 'react';
import { useDispatch, useSelect } from '@wordpress/data';
import { partial } from 'lodash';
import BasicSettingsPanel from './basic_settings_panel.jsx';
import StylesSettingsPanel from './styles_settings_panel.jsx';
import FormPlacementPanel from './form_placement_panel.jsx';
import CustomCssPanel from './custom_css_panel.jsx';
@ -19,6 +20,10 @@ export default () => {
isOpened={openedPanels.includes('basic-settings')}
onToggle={partial(toggleSidebarPanel, 'basic-settings')}
/>
<StylesSettingsPanel
isOpened={openedPanels.includes('styles-settings')}
onToggle={partial(toggleSidebarPanel, 'styles-settings')}
/>
<FormPlacementPanel
isOpened={openedPanels.includes('form-placement')}
onToggle={partial(toggleSidebarPanel, 'form-placement')}

View File

@ -0,0 +1,114 @@
import React from 'react';
import {
ColorIndicator,
ColorPalette,
FontSizePicker,
Panel,
PanelBody,
} from '@wordpress/components';
import MailPoet from 'mailpoet';
import PropTypes from 'prop-types';
import { useSelect, useDispatch } from '@wordpress/data';
const BasicSettingsPanel = ({ onToggle, isOpened }) => {
const { changeFormSettings } = useDispatch('mailpoet-form-editor');
const settings = useSelect(
(select) => select('mailpoet-form-editor').getFormSettings(),
[]
);
const setBackgroundColor = (color) => {
changeFormSettings({
...settings,
backgroundColor: color,
});
};
const setFontColor = (color) => {
changeFormSettings({
...settings,
fontColor: color,
});
};
const setFontSize = (size) => {
changeFormSettings({
...settings,
fontSize: size,
});
};
const { settingsColors, fontSizes } = useSelect(
(select) => {
const { getSettings } = select('core/block-editor');
return {
settingsColors: getSettings().colors,
fontSizes: getSettings().fontSizes,
};
},
[]
);
return (
<Panel>
<PanelBody
title={MailPoet.I18n.t('formSettingsStyles')}
opened={isOpened}
onToggle={onToggle}
>
<div className="mailpoet-styles-settings">
<div>
<h3 className="mailpoet-styles-settings-heading">
{MailPoet.I18n.t('formSettingsStylesBackgroundColor')}
{
settings.backgroundColor !== undefined
&& (
<ColorIndicator
colorValue={settings.backgroundColor}
/>
)
}
</h3>
<ColorPalette
value={settings.backgroundColor}
onChange={setBackgroundColor}
colors={settingsColors}
/>
</div>
<div>
<h3 className="mailpoet-styles-settings-heading">
{MailPoet.I18n.t('formSettingsStylesFontColor')}
{
settings.fontColor !== undefined
&& (
<ColorIndicator
colorValue={settings.fontColor}
/>
)
}
</h3>
<ColorPalette
value={settings.fontColor}
onChange={setFontColor}
colors={settingsColors}
/>
</div>
<div>
<h3 className="mailpoet-styles-settings-heading">
{MailPoet.I18n.t('formSettingsStylesFontSize')}
</h3>
<FontSizePicker
value={settings.fontSize}
onChange={setFontSize}
fontSizes={fontSizes}
/>
</div>
</div>
</PanelBody>
</Panel>
);
};
BasicSettingsPanel.propTypes = {
onToggle: PropTypes.func.isRequired,
isOpened: PropTypes.bool.isRequired,
};
export default BasicSettingsPanel;

View File

@ -0,0 +1,38 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelect } from '@wordpress/data';
const FormStylingBackground = ({ children }) => {
const { fontColor, backgroundColor, fontSize } = useSelect(
(select) => {
const settings = select('mailpoet-form-editor').getFormSettings();
return {
backgroundColor: settings.backgroundColor,
fontColor: settings.fontColor,
fontSize: settings.fontSize,
};
},
[]
);
let font;
if (fontSize) font = Number(fontSize);
return (
<div
style={{
backgroundColor,
color: fontColor,
fontSize: font,
lineHeight: 1.2,
}}
>
{children}
</div>
);
};
FormStylingBackground.propTypes = {
children: PropTypes.node.isRequired,
};
export default FormStylingBackground;

View File

@ -1,4 +1,10 @@
import React, { useEffect, useState, useCallback } from 'react';
import React, {
useEffect,
useState,
useCallback,
useRef,
useLayoutEffect,
} from 'react';
import MailPoet from 'mailpoet';
import { Spinner } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
@ -8,6 +14,7 @@ import Modal from '../../common/modal/modal.jsx';
import blocksToFormBody from '../store/blocks_to_form_body.jsx';
const FormPreview = () => {
const formEl = useRef(null);
const [form, setForm] = useState(null);
const formBlocks = useSelect(
@ -18,6 +25,10 @@ const FormPreview = () => {
(select) => select('mailpoet-form-editor').getAllAvailableCustomFields(),
[]
);
const settings = useSelect(
(select) => select('mailpoet-form-editor').getFormSettings(),
[]
);
const { hidePreview } = useDispatch('mailpoet-form-editor');
const isPreview = useSelect(
@ -32,11 +43,12 @@ const FormPreview = () => {
action: 'previewEditor',
data: {
body: blocksToFormBody(formBlocks, customFields),
settings,
},
}).done((response) => {
setForm(response.data);
});
}, [formBlocks, customFields]);
}, [formBlocks, customFields, settings]);
useEffect(() => {
if (isPreview) {
@ -44,6 +56,13 @@ const FormPreview = () => {
}
}, [isPreview, loadFormPreviewFromServer]);
useLayoutEffect(() => {
// eslint-disable-next-line camelcase
if (formEl.current && form?.form_element_styles) {
formEl.current.setAttribute('style', form.form_element_styles);
}
}, [formEl, form]);
if (!isPreview) return null;
function onClose() {
@ -69,12 +88,20 @@ const FormPreview = () => {
{'.mailpoet_hp_email_label { display: none }' }
{form.css}
</style>
{/* eslint-disable-next-line react/no-danger */}
<div dangerouslySetInnerHTML={{ __html: form.html }} />
<div className="mailpoet_message">
<p className="mailpoet_validate_success">{MailPoet.I18n.t('successMessage')}</p>
<p className="mailpoet_validate_error">{MailPoet.I18n.t('errorMessage')}</p>
</div>
<form
target="_self"
method="post"
className="mailpoet_form "
noValidate
ref={formEl}
>
{/* eslint-disable-next-line react/no-danger */}
<div dangerouslySetInnerHTML={{ __html: form.html }} />
<div className="mailpoet_message">
<p className="mailpoet_validate_success">{MailPoet.I18n.t('successMessage')}</p>
<p className="mailpoet_validate_error">{MailPoet.I18n.t('errorMessage')}</p>
</div>
</form>
</div>
</Preview>
)}

View File

@ -13,6 +13,7 @@ import ListingFilters from 'listing/filters.jsx';
import ListingItems from 'listing/listing_items.jsx';
import MailerError from 'listing/notices.jsx';
import { withRouter } from 'react-router-dom';
import { GlobalContext } from 'context/index.jsx';
class Listing extends React.Component {
constructor(props) {
@ -190,8 +191,8 @@ class Listing extends React.Component {
});
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -266,8 +267,8 @@ class Listing extends React.Component {
}
this.getItems();
}).fail((response) => {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
});
@ -295,8 +296,8 @@ class Listing extends React.Component {
}
this.getItems();
}).fail((response) => {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
});
@ -324,8 +325,8 @@ class Listing extends React.Component {
}
this.getItems();
}).fail((response) => {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
});
@ -344,8 +345,8 @@ class Listing extends React.Component {
// redirect to default group
this.handleGroup('all');
}).fail((response) => {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
});
@ -383,8 +384,8 @@ class Listing extends React.Component {
this.getItems();
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -685,6 +686,8 @@ class Listing extends React.Component {
}
}
Listing.contextType = GlobalContext;
/* eslint-disable react/require-default-props */
Listing.propTypes = {
limit: PropTypes.number,

View File

@ -6,7 +6,7 @@ import Marionette from 'backbone.marionette';
import $ from 'jquery';
import Blob from 'blob';
import FileSaver from 'file-saver';
import * as Thumbnail from 'common/thumbnail.jsx';
import * as Thumbnail from 'common/thumbnail.ts';
import _ from 'underscore';
var Module = {};

View File

@ -6,6 +6,7 @@ import EventOptions from 'newsletters/automatic_emails/events/event_options.jsx'
import MailPoet from 'mailpoet';
import _ from 'underscore';
import PropTypes from 'prop-types';
import { GlobalContext } from 'context/index.jsx';
const defaultAfterTimeType = 'immediate';
const defaultAfterTimeNumber = 1;
@ -205,8 +206,8 @@ class EventsConditions extends React.Component {
history.push(`/template/${response.data.id}`);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -245,6 +246,8 @@ class EventsConditions extends React.Component {
}
}
EventsConditions.contextType = GlobalContext;
EventsConditions.propTypes = {
history: PropTypes.shape({
push: PropTypes.func.isRequired,

View File

@ -3,7 +3,7 @@ import MailPoet from 'mailpoet';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Link } from 'react-router-dom';
import APIErrorsNotice from 'notices/api_errors_notice.jsx';
import APIErrorsNotice from 'notices/api_errors_notice.tsx';
const QueuePropType = PropTypes.shape({
status: PropTypes.string,

View File

@ -8,13 +8,14 @@ import NotificationNewsletterFields from 'newsletters/send/notification.jsx';
import WelcomeNewsletterFields from 'newsletters/send/welcome.jsx';
import HelpTooltip from 'help-tooltip.jsx';
import jQuery from 'jquery';
import { fromUrl } from 'common/thumbnail.jsx';
import { fromUrl } from 'common/thumbnail.ts';
import Hooks from 'wp-js-hooks';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import ReactStringReplace from 'react-string-replace';
import SubscribersLimitNotice from 'notices/subscribers_limit_notice.jsx';
import slugify from 'slugify';
import { GlobalContext } from 'context/index.jsx';
const generateGaTrackingCampaignName = (id, subject) => {
const name = slugify(subject, { lower: true })
@ -242,16 +243,16 @@ class NewsletterSend extends React.Component {
if (_.isFunction(customResponse)) {
customResponse();
} else if (response.data.status === 'scheduled') {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterHasBeenScheduled')
this.context.notices.success(
<p>{MailPoet.I18n.t('newsletterHasBeenScheduled')}</p>
);
MailPoet.trackEvent('Emails > Newsletter sent', {
scheduled: true,
'MailPoet Free version': window.mailpoet_version,
});
} else {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterBeingSent'),
this.context.notices.success(
<p>{MailPoet.I18n.t('newsletterBeingSent')}</p>,
{ id: 'mailpoet_notice_being_sent' }
);
MailPoet.trackEvent('Emails > Newsletter sent', {
@ -288,8 +289,8 @@ class NewsletterSend extends React.Component {
const opts = this.state.item.options;
// display success message depending on newsletter type
if (response.data.type === 'welcome') {
MailPoet.Notice.success(
MailPoet.I18n.t('welcomeEmailActivated')
this.context.notices.success(
<p>{MailPoet.I18n.t('welcomeEmailActivated')}</p>
);
MailPoet.trackEvent('Emails > Welcome email activated', {
'MailPoet Free version': window.mailpoet_version,
@ -297,8 +298,8 @@ class NewsletterSend extends React.Component {
Delay: `${opts.afterTimeNumber} ${opts.afterTimeType}`,
});
} else if (response.data.type === 'notification') {
MailPoet.Notice.success(
MailPoet.I18n.t('postNotificationActivated')
this.context.notices.success(
<p>{MailPoet.I18n.t('postNotificationActivated')}</p>
);
MailPoet.trackEvent('Emails > Post notifications activated', {
'MailPoet Free version': window.mailpoet_version,
@ -330,16 +331,11 @@ class NewsletterSend extends React.Component {
},
}).done(() => {
this.props.history.push(`/${this.state.item.type || ''}`);
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterSendingHasBeenResumed')
this.context.notices.success(
<p>{MailPoet.I18n.t('newsletterSendingHasBeenResumed')}</p>
);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
{ scroll: true }
);
}
this.showError(response);
});
})
.fail((err) => {
@ -356,8 +352,8 @@ class NewsletterSend extends React.Component {
e.preventDefault();
this.saveNewsletter(e).done(() => {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterUpdated')
this.context.notices.success(
<p>{MailPoet.I18n.t('newsletterUpdated')}</p>
);
}).done(() => {
const path = this.state.item.type === 'automatic' ? this.state.item.options.group : this.state.item.type;
@ -372,8 +368,8 @@ class NewsletterSend extends React.Component {
const redirectTo = e.target.href;
this.saveNewsletter(e).done(() => {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterUpdated')
this.context.notices.success(
<p>{MailPoet.I18n.t('newsletterUpdated')}</p>
);
}).done(() => {
window.location = redirectTo;
@ -407,8 +403,8 @@ class NewsletterSend extends React.Component {
showError = (response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -528,6 +524,8 @@ class NewsletterSend extends React.Component {
}
}
NewsletterSend.contextType = GlobalContext;
NewsletterSend.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({

View File

@ -8,6 +8,7 @@ import ImportTemplate from 'newsletters/templates/import_template.jsx';
import Hooks from 'wp-js-hooks';
import _ from 'underscore';
import PropTypes from 'prop-types';
import { GlobalContext } from 'context/index.jsx';
const getEditorUrl = (id) => `admin.php?page=mailpoet-newsletter-editor&id=${id}`;
@ -87,8 +88,8 @@ class NewsletterTemplates extends React.Component {
this.sortTemplates();
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -158,8 +159,8 @@ class NewsletterTemplates extends React.Component {
}
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -277,6 +278,8 @@ class NewsletterTemplates extends React.Component {
}
}
NewsletterTemplates.contextType = GlobalContext;
NewsletterTemplates.propTypes = {
match: PropTypes.shape({
params: PropTypes.shape({

View File

@ -3,6 +3,7 @@ import _ from 'underscore';
import MailPoet from 'mailpoet';
import HelpTooltip from 'help-tooltip.jsx';
import PropTypes from 'prop-types';
import { GlobalContext } from 'context/index.jsx';
class ImportTemplate extends React.Component {
constructor(props) {
@ -51,8 +52,8 @@ class ImportTemplate extends React.Component {
afterImport(true, response.data);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -77,7 +78,7 @@ class ImportTemplate extends React.Component {
'MailPoet Free version': window.mailpoet_version,
});
} catch (err) {
MailPoet.Notice.error(MailPoet.I18n.t('templateFileMalformedError'));
this.context.notices.error(<p>{MailPoet.I18n.t('templateFileMalformedError')}</p>);
}
};
@ -115,6 +116,8 @@ class ImportTemplate extends React.Component {
}
}
ImportTemplate.contextType = GlobalContext;
ImportTemplate.propTypes = {
beforeImport: PropTypes.func.isRequired,
afterImport: PropTypes.func.isRequired,

View File

@ -3,6 +3,7 @@ import MailPoet from 'mailpoet';
import PropTypes from 'prop-types';
import confirmAlert from 'common/confirm_alert.jsx';
import { GlobalContext } from 'context/index.jsx';
/**
* props = {
@ -35,8 +36,8 @@ class TemplateBox extends React.Component {
afterDelete(true, id);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -81,8 +82,8 @@ class TemplateBox extends React.Component {
afterSelect(true, response.data.id);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -158,6 +159,8 @@ class TemplateBox extends React.Component {
}
}
TemplateBox.contextType = GlobalContext;
TemplateBox.propTypes = {
index: PropTypes.number.isRequired,
id: PropTypes.string.isRequired,

View File

@ -5,6 +5,7 @@ import Breadcrumb from 'newsletters/breadcrumb.jsx';
import Hooks from 'wp-js-hooks';
import _ from 'underscore';
import { withRouter } from 'react-router-dom';
import { GlobalContext } from 'context/index.jsx';
class NewsletterTypes extends React.Component {
constructor(props) {
@ -108,7 +109,12 @@ class NewsletterTypes extends React.Component {
'MailPoet Free version': window.mailpoet_version,
});
} catch (response) {
MailPoet.Notice.showApiErrorNotice(response, { scroll: true });
if (response.errors.length > 0) {
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
return;
}
}
@ -134,8 +140,8 @@ class NewsletterTypes extends React.Component {
}).fail((response) => {
this.setState({ isCreating: false });
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -289,6 +295,8 @@ class NewsletterTypes extends React.Component {
}
}
NewsletterTypes.contextType = GlobalContext;
NewsletterTypes.propTypes = {
filter: PropTypes.func,
history: PropTypes.shape({

View File

@ -5,6 +5,7 @@ import Breadcrumb from 'newsletters/breadcrumb.jsx';
import _ from 'underscore';
import Scheduling from 'newsletters/types/notification/scheduling.jsx';
import { withRouter } from 'react-router-dom';
import { GlobalContext } from 'context/index.jsx';
const field = {
name: 'options',
@ -45,8 +46,8 @@ class NewsletterNotification extends React.Component {
this.showTemplateSelection(response.data.id);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -84,6 +85,8 @@ class NewsletterNotification extends React.Component {
}
}
NewsletterNotification.contextType = GlobalContext;
NewsletterNotification.propTypes = {
history: PropTypes.shape({
push: PropTypes.func.isRequired,

View File

@ -3,6 +3,7 @@ import React from 'react';
import MailPoet from 'mailpoet';
import Breadcrumb from 'newsletters/breadcrumb.jsx';
import { withRouter } from 'react-router-dom';
import { GlobalContext } from 'context/index.jsx';
class NewsletterStandard extends React.Component {
componentDidMount() {
@ -18,8 +19,8 @@ class NewsletterStandard extends React.Component {
this.showTemplateSelection(response.data.id);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map((error) => error.message),
this.context.notices.error(
response.errors.map((error) => <p key={error.message}>{error.message}</p>),
{ scroll: true }
);
}
@ -40,6 +41,8 @@ class NewsletterStandard extends React.Component {
}
}
NewsletterStandard.contextType = GlobalContext;
NewsletterStandard.propTypes = {
history: PropTypes.shape({
push: PropTypes.func.isRequired,

View File

@ -1,15 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Notice from 'notices/notice.jsx';
const APIErrorsNotice = ({ errors }) => {
if (errors.length < 1) return null;
return <Notice type="error" closable={false}>{errors.map((err) => <p key={err.message}>{err.message}</p>)}</Notice>;
};
APIErrorsNotice.propTypes = {
errors: PropTypes.arrayOf(PropTypes.shape({
message: PropTypes.string.isRequired,
})).isRequired,
};
export default APIErrorsNotice;

View File

@ -0,0 +1,13 @@
import React from 'react';
import Notice from 'notices/notice';
type Props = {
errors: Array<{ message: string }>
}
const APIErrorsNotice = ({ errors }: Props) => {
if (errors.length < 1) return null;
return <Notice type="error" closable={false}>{errors.map((err) => <p key={err.message}>{err.message}</p>)}</Notice>;
};
export default APIErrorsNotice;

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import Notice from 'notices/notice.jsx';
import Notice from 'notices/notice.tsx';
const MailerStatusNotice = ({ error }) => {
if (!error || error.operation !== 'authorization') return null;

View File

@ -1,8 +1,18 @@
import React from 'react';
import React, { ReactNode } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import MailPoet from 'mailpoet';
type Props = {
type: 'success' | 'info' | 'warning' | 'error';
children: ReactNode;
scroll?: boolean;
closable?: boolean;
renderInPlace?: boolean;
onDisplay?: () => void;
onClose?: () => void;
timeout?: number | false;
};
const Notice = ({
onClose,
onDisplay,
@ -12,7 +22,7 @@ const Notice = ({
children,
closable,
type,
}) => {
}: Props) => {
const [hidden, setHidden] = React.useState(false);
const elementRef = React.useRef(null);
const timeoutRef = React.useRef(null);
@ -24,7 +34,7 @@ const Notice = ({
React.useEffect(() => {
if (timeout) {
timeoutRef.current = setTimeout(close, timeout);
timeoutRef.current = setTimeout(close, timeout as number);
}
return () => (timeoutRef.current ? clearTimeout(timeoutRef.current) : null);
}, [close, timeout]);
@ -61,20 +71,6 @@ const Notice = ({
document.getElementById('mailpoet_notices')
);
};
Notice.propTypes = {
type: PropTypes.oneOf(['success', 'info', 'warning', 'error']).isRequired,
scroll: PropTypes.bool,
closable: PropTypes.bool,
renderInPlace: PropTypes.bool,
onDisplay: PropTypes.func,
onClose: PropTypes.func,
timeout: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf([false])]),
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
PropTypes.arrayOf(PropTypes.element),
]).isRequired,
};
Notice.defaultProps = {
timeout: 10000,
scroll: false,

View File

@ -1,6 +1,6 @@
import React from 'react';
import { GlobalContext } from 'context/index.jsx';
import Notice from './notice.jsx';
import Notice from './notice.tsx';
export default () => {
const { notices } = React.useContext(GlobalContext);

View File

@ -1,6 +1,6 @@
import React from 'react';
import MailPoet from 'mailpoet';
import Notice from 'notices/notice.jsx';
import Notice from 'notices/notice.tsx';
const SubscribersLimitNotice = () => {
if (!window.mailpoet_subscribers_limit_reached) return null;

View File

@ -119,6 +119,7 @@ class Forms extends APIEndpoint {
return $this->successResponse([
'html' => $html,
'css' => $css,
'form_element_styles' => $this->formRenderer->renderFormElementStyles($data),
]);
}

View File

@ -82,7 +82,7 @@ class BlockRendererHelper {
return join(' ', $validation);
}
public function renderLabel(array $block): string {
public function renderLabel(array $block, array $formSettings): string {
$html = '';
if (
isset($block['params']['hide_label'])
@ -98,7 +98,10 @@ class BlockRendererHelper {
}
if (isset($block['params']['label'])
&& strlen(trim($block['params']['label'])) > 0) {
$html .= '<label class="mailpoet_' . $block['type'] . '_label">';
$html .= '<label '
. 'class="mailpoet_' . $block['type'] . '_label" '
. $this->renderFontStyle($formSettings)
. '>';
$html .= htmlspecialchars($block['params']['label']);
if (isset($block['params']['required']) && $block['params']['required']) {
@ -110,6 +113,15 @@ class BlockRendererHelper {
return $html;
}
public function renderFontStyle(array $formSettings) {
if (isset($formSettings['fontSize'])) {
return 'style="'
. 'font-size: ' . trim($formSettings['fontSize']) . 'px;'
. 'line-height: ' . trim($formSettings['fontSize']) * 1.2 . 'px";';
}
return '';
}
public function renderInputPlaceholder(array $block): string {
$html = '';
// if the label is displayed as a placeholder,

View File

@ -17,7 +17,7 @@ class Checkbox {
$this->wp = $wp;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$html = '';
$fieldName = 'data[' . $this->rendererHelper->getFieldName($block) . ']';
@ -25,7 +25,7 @@ class Checkbox {
$html .= '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$options = (!empty($block['params']['values'])
? $block['params']['values']
@ -35,7 +35,8 @@ class Checkbox {
$selectedValue = $this->rendererHelper->getFieldValue($block);
foreach ($options as $option) {
$html .= '<label class="mailpoet_checkbox_label">';
$html .= '<label class="mailpoet_checkbox_label" '
. $this->rendererHelper->renderFontStyle($formSettings) . '>';
$html .= '<input type="checkbox" class="mailpoet_checkbox" ';
$html .= 'name="' . $fieldName . '" ';

View File

@ -11,10 +11,10 @@ class Date {
$this->rendererHelper = $rendererHelper;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$html = '';
$html .= '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$html .= $this->renderDateSelect($block);
$html .= '</p>';

View File

@ -3,7 +3,14 @@
namespace MailPoet\Form\Block;
class Html {
public function render(array $block): string {
/** @var BlockRendererHelper */
private $rendererHelper;
public function __construct(BlockRendererHelper $rendererHelper) {
$this->rendererHelper = $rendererHelper;
}
public function render(array $block, array $formSettings): string {
$html = '';
$text = '';
@ -15,7 +22,7 @@ class Html {
$text = nl2br($text);
}
$html .= '<p class="mailpoet_paragraph">';
$html .= '<p class="mailpoet_paragraph" ' . $this->rendererHelper->renderFontStyle($formSettings) . '>';
$html .= $text;
$html .= '</p>';

View File

@ -17,7 +17,7 @@ class Radio {
$this->wp = $wp;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$html = '';
$fieldName = 'data[' . $this->rendererHelper->getFieldName($block) . ']';
@ -25,7 +25,7 @@ class Radio {
$html .= '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$options = (!empty($block['params']['values'])
? $block['params']['values']
@ -35,7 +35,9 @@ class Radio {
$selectedValue = $this->rendererHelper->getFieldValue($block);
foreach ($options as $option) {
$html .= '<label class="mailpoet_radio_label">';
$html .= '<label class="mailpoet_radio_label" '
. $this->rendererHelper->renderFontStyle($formSettings)
. '>';
$html .= '<input type="radio" class="mailpoet_radio" ';

View File

@ -17,7 +17,7 @@ class Segment {
$this->wp = $wp;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$html = '';
$fieldName = 'data[' . $this->rendererHelper->getFieldName($block) . ']';
@ -25,7 +25,7 @@ class Segment {
$html .= '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$options = (!empty($block['params']['values'])
? $block['params']['values']
@ -37,7 +37,9 @@ class Segment {
$isChecked = (isset($option['is_checked']) && $option['is_checked']) ? 'checked="checked"' : '';
$html .= '<label class="mailpoet_checkbox_label">';
$html .= '<label class="mailpoet_checkbox_label" '
. $this->rendererHelper->renderFontStyle($formSettings)
. '>';
$html .= '<input type="checkbox" class="mailpoet_checkbox" ';
$html .= 'name="' . $fieldName . '[]" ';
$html .= 'value="' . $option['id'] . '" ' . $isChecked . ' ';

View File

@ -17,13 +17,13 @@ class Select {
$this->wp = $wp;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$html = '';
$fieldName = 'data[' . $this->rendererHelper->getFieldName($block) . ']';
$automationId = ($block['id'] == 'status') ? 'data-automation-id="form_status"' : '';
$html .= '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$html .= '<select class="mailpoet_select" name="' . $fieldName . '" ' . $automationId . '>';
if (isset($block['params']['label_within']) && $block['params']['label_within']) {

View File

@ -11,7 +11,7 @@ class Text {
$this->rendererHelper = $rendererHelper;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$type = 'text';
$automationId = ' ';
if ($block['id'] === 'email') {
@ -21,7 +21,7 @@ class Text {
$html = '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$html .= '<input type="' . $type . '" class="mailpoet_text" ';

View File

@ -10,12 +10,12 @@ class Textarea {
$this->rendererHelper = $rendererHelper;
}
public function render(array $block): string {
public function render(array $block, array $formSettings): string {
$html = '';
$html .= '<p class="mailpoet_paragraph">';
$html .= $this->rendererHelper->renderLabel($block);
$html .= $this->rendererHelper->renderLabel($block, $formSettings);
$lines = (isset($block['params']['lines']) ? (int)$block['params']['lines'] : 1);

View File

@ -68,11 +68,11 @@ class BlocksRenderer {
$this->textarea = $textarea;
}
public function renderBlock(array $block = []): string {
public function renderBlock(array $block = [], array $formSettings): string {
$html = '';
switch ($block['type']) {
case 'html':
$html .= $this->html->render($block);
$html .= $this->html->render($block, $formSettings);
break;
case 'divider':
@ -80,31 +80,31 @@ class BlocksRenderer {
break;
case 'checkbox':
$html .= $this->checkbox->render($block);
$html .= $this->checkbox->render($block, $formSettings);
break;
case 'radio':
$html .= $this->radio->render($block);
$html .= $this->radio->render($block, $formSettings);
break;
case 'segment':
$html .= $this->segment->render($block);
$html .= $this->segment->render($block, $formSettings);
break;
case 'date':
$html .= $this->date->render($block);
$html .= $this->date->render($block, $formSettings);
break;
case 'select':
$html .= $this->select->render($block);
$html .= $this->select->render($block, $formSettings);
break;
case 'text':
$html .= $this->text->render($block);
$html .= $this->text->render($block, $formSettings);
break;
case 'textarea':
$html .= $this->textarea->render($block);
$html .= $this->textarea->render($block, $formSettings);
break;
case 'submit':

View File

@ -94,15 +94,18 @@ class DisplayFormInWPContent {
$formData = [
'body' => $form->getBody(),
'styles' => $form->getStyles(),
'settings' => $form->getSettings(),
];
$formSettings = $form->getSettings();
$htmlId = 'mp_form_below_' . $form->getId();
$templateData = [
'form_html_id' => 'mp_form_below_' . $form->getId(),
'form_html_id' => $htmlId,
'form_id' => $form->getId(),
'form_success_message' => $formSettings['success_message'] ?? null,
'form_type' => 'below_post',
'styles' => $this->formRenderer->renderStyles($formData, '#' . $form->getId()),
'styles' => $this->formRenderer->renderStyles($formData, '#' . $htmlId),
'html' => $this->formRenderer->renderHTML($formData),
'form_element_styles' => $this->formRenderer->renderFormElementStyles($formData),
];
// (POST) non ajax success/error variables

View File

@ -37,7 +37,7 @@ class Renderer {
public function renderHTML(array $form = []): string {
if (isset($form['body']) && !empty($form['body'])) {
return $this->renderBlocks($form['body']);
return $this->renderBlocks($form['body'], $form['settings'] ?? []);
}
return '';
}
@ -51,14 +51,14 @@ class Renderer {
}
}
public function renderBlocks(array $blocks = [], bool $honeypotEnabled = true): string {
public function renderBlocks(array $blocks = [], array $formSettings = [], bool $honeypotEnabled = true): string {
// add honeypot for spambots
$html = ($honeypotEnabled) ? $this->renderHoneypot() : '';
foreach ($blocks as $key => $block) {
if ($block['type'] == 'submit' && $this->settings->get('captcha.type') === Captcha::TYPE_RECAPTCHA) {
$html .= $this->renderReCaptcha();
}
$html .= $this->blocksRenderer->renderBlock($block) . PHP_EOL;
$html .= $this->blocksRenderer->renderBlock($block, $formSettings) . PHP_EOL;
}
return $html;
}
@ -88,4 +88,20 @@ class Renderer {
<input class="mailpoet_recaptcha_field" type="hidden" name="recaptcha">
</div>';
}
public function renderFormElementStyles(array $form): string {
if (!isset($form['settings'])) return '';
$formSettings = $form['settings'];
$styles = [];
if (isset($formSettings['backgroundColor'])) {
$styles[] = 'background-color: ' . trim($formSettings['backgroundColor']);
}
if (isset($formSettings['fontColor'])) {
$styles[] = 'color: ' . trim($formSettings['fontColor']);
}
return join(';', $styles);
}
}

View File

@ -8,7 +8,7 @@ class Styles {
private $defaultStyles = <<<EOL
/* form */
.mailpoet_form {
padding: 10px;
}
/* paragraphs (label + input) */
@ -48,7 +48,7 @@ class Styles {
.mailpoet_checkbox {
}
.mailpoet_submit input {
.mailpoet_submit {
}
.mailpoet_divider {

View File

@ -226,6 +226,7 @@ class Widget extends \WP_Widget {
'after_widget' => $afterWidget,
'before_title' => $beforeTitle,
'after_title' => $afterTitle,
'form_element_styles' => $this->formRenderer->renderFormElementStyles($form),
];
// (POST) non ajax success/error variables

View File

@ -115,7 +115,7 @@ class CaptchaRenderer {
$formHtml .= '</p>';
// subscription form
$formHtml .= $this->formRenderer->renderBlocks($form, $honeypot = false);
$formHtml .= $this->formRenderer->renderBlocks($form, [], $honeypot = false);
$formHtml .= '</div>';
$formHtml .= $this->renderFormMessages($formModel, $showSuccessMessage, $showErrorMessage);
$formHtml .= '</form>';

View File

@ -497,7 +497,7 @@ class Pages {
$formHtml .= '</p>';
// subscription form
$formHtml .= $this->formRenderer->renderBlocks($form, $honeypot = false);
$formHtml .= $this->formRenderer->renderBlocks($form, [], $honeypot = false);
$formHtml .= '</form>';
return $formHtml;
}

View File

@ -2,7 +2,7 @@
/*
* Plugin Name: MailPoet 3 (New)
* Version: 3.46.0
* Version: 3.46.2
* Plugin URI: http://www.mailpoet.com
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
* Author: MailPoet
@ -16,7 +16,7 @@
*/
$mailpoetPlugin = [
'version' => '3.46.0',
'version' => '3.46.2',
'filename' => __FILE__,
'path' => dirname(__FILE__),
'autoloader' => dirname(__FILE__) . '/vendor/autoload.php',

343
package-lock.json generated
View File

@ -2408,6 +2408,23 @@
}
}
},
"@babel/plugin-syntax-typescript": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.8.3.tgz",
"integrity": "sha512-GO1MQ/SGGGoiEXY0e0bSpHimJvxqB7lktLLIq2pv8xG7WZ8IMEle74jIe1FhprHBWjwjZtXHkycDLZXIWM5Wfg==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.3"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
"integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
"dev": true
}
}
},
"@babel/plugin-transform-arrow-functions": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz",
@ -3283,6 +3300,25 @@
}
}
},
"@babel/plugin-transform-typescript": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.8.3.tgz",
"integrity": "sha512-Ebj230AxcrKGZPKIp4g4TdQLrqX95TobLUWKd/CwG7X1XHUH1ZpkpFvXuXqWbtGRWb7uuEWNlrl681wsOArAdQ==",
"dev": true,
"requires": {
"@babel/helper-create-class-features-plugin": "^7.8.3",
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/plugin-syntax-typescript": "^7.8.3"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
"integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
"dev": true
}
}
},
"@babel/plugin-transform-unicode-regex": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz",
@ -3449,6 +3485,24 @@
}
}
},
"@babel/preset-typescript": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.8.3.tgz",
"integrity": "sha512-qee5LgPGui9zQ0jR1TeU5/fP9L+ovoArklEqY12ek8P/wV5ZeM/VYSQYwICeoT6FfpJTekG9Ilay5PhwsOpMHA==",
"dev": true,
"requires": {
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/plugin-transform-typescript": "^7.8.3"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
"integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
"dev": true
}
}
},
"@babel/register": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.8.3.tgz",
@ -3812,9 +3866,9 @@
}
},
"@popperjs/core": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.0.5.tgz",
"integrity": "sha512-YOV1TitTNzJDXe/14sDJO/M/aL12Jhind0EkQRnqTX2167fqJsAICJfi0vsDdapPI1WaYsheyYYgy6PO02Nqqg=="
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.0.6.tgz",
"integrity": "sha512-zj7Gw8QC4jmR92eKUvtrZUEpl2ypRbq+qlE4pwf9n2hnUO9BOAcWUs4/Ht+gNIbFt98xtqhLvccdCfD469MzpQ=="
},
"@sinonjs/commons": {
"version": "1.7.0",
@ -3885,6 +3939,12 @@
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
"dev": true
},
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
"dev": true
},
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
@ -3902,6 +3962,12 @@
"@types/node": "*"
}
},
"@types/json-schema": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
"integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -3931,6 +3997,22 @@
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
},
"@types/prop-types": {
"version": "15.7.3",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
"dev": true
},
"@types/react": {
"version": "16.9.22",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.22.tgz",
"integrity": "sha512-7OSt4EGiLvy0h5R7X+r0c7S739TCU/LvWbkNOrm10lUwNHe7XPz5OLhLOSZeCkqO9JSCly1NkYJ7ODTUqVnHJQ==",
"dev": true,
"requires": {
"@types/prop-types": "*",
"csstype": "^2.2.0"
}
},
"@types/unist": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz",
@ -3957,6 +4039,137 @@
"vfile-message": "*"
}
},
"@typescript-eslint/eslint-plugin": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.21.0.tgz",
"integrity": "sha512-b5jjjDMxzcjh/Sbjuo7WyhrQmVJg0WipTHQgXh5Xwx10uYm6nPWqN1WGOsaNq4HR3Zh4wUx4IRQdDkCHwyewyw==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "2.21.0",
"eslint-utils": "^1.4.3",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
"tsutils": "^3.17.1"
},
"dependencies": {
"regexpp": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz",
"integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==",
"dev": true
}
}
},
"@typescript-eslint/experimental-utils": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.21.0.tgz",
"integrity": "sha512-olKw9JP/XUkav4lq0I7S1mhGgONJF9rHNhKFn9wJlpfRVjNo3PPjSvybxEldvCXnvD+WAshSzqH5cEjPp9CsBA==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "2.21.0",
"eslint-scope": "^5.0.0"
},
"dependencies": {
"eslint-scope": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
"integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
"dev": true,
"requires": {
"esrecurse": "^4.1.0",
"estraverse": "^4.1.1"
}
}
}
},
"@typescript-eslint/parser": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.21.0.tgz",
"integrity": "sha512-VrmbdrrrvvI6cPPOG7uOgGUFXNYTiSbnRq8ZMyuGa4+qmXJXVLEEz78hKuqupvkpwJQNk1Ucz1TenrRP90gmBg==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "2.21.0",
"@typescript-eslint/typescript-estree": "2.21.0",
"eslint-visitor-keys": "^1.1.0"
},
"dependencies": {
"eslint-visitor-keys": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
"integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
"dev": true
}
}
},
"@typescript-eslint/typescript-estree": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.21.0.tgz",
"integrity": "sha512-NC/nogZNb9IK2MEFQqyDBAciOT8Lp8O3KgAfvHx2Skx6WBo+KmDqlU3R9KxHONaijfTIKtojRe3SZQyMjr3wBw==",
"dev": true,
"requires": {
"debug": "^4.1.1",
"eslint-visitor-keys": "^1.1.0",
"glob": "^7.1.6",
"is-glob": "^4.0.1",
"lodash": "^4.17.15",
"semver": "^6.3.0",
"tsutils": "^3.17.1"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"eslint-visitor-keys": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
"integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
"dev": true
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"dev": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"is-glob": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
"integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
"dev": true,
"requires": {
"is-extglob": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
}
}
},
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@ -4169,14 +4382,14 @@
}
},
"@wordpress/block-directory": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@wordpress/block-directory/-/block-directory-1.5.0.tgz",
"integrity": "sha512-LxVOYsWFNhVIIFgVLChBWWo+g/tUT4UKBvN4IF9afoOmF6SYp9VDHuEfQzUO/FeWlyuYK2gvcqYOdhzLnw7BuA==",
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/@wordpress/block-directory/-/block-directory-1.5.1.tgz",
"integrity": "sha512-YdlatnSHjw0dKxr4mqKT10awrVYC//kHrwtRrfg2z98+O+4uVsWN9E0mtHzFmYQHRFK3Ep/+GqIgp2RyH8TMgQ==",
"requires": {
"@wordpress/api-fetch": "^3.11.0",
"@wordpress/block-editor": "^3.7.0",
"@wordpress/block-editor": "^3.7.1",
"@wordpress/blocks": "^6.12.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/compose": "^3.11.0",
"@wordpress/data": "^4.14.0",
"@wordpress/element": "^2.11.0",
@ -4187,15 +4400,15 @@
}
},
"@wordpress/block-editor": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-3.7.0.tgz",
"integrity": "sha512-8qKVQUeXbRctCnBVmcg+G/uc8+LGaGOT++iD0FRGK3C3HjMQ1qWiwhUFvXZukMUWQaiw5tJWGMOxmVb3TI4Z5A==",
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-3.7.1.tgz",
"integrity": "sha512-joZBiYY4iqSpyFJBsPvEJQjILxdywESTu4ACAKb8SGQ19DIa3vkuS6FTEsmdy+YfkRiohhPAsd17mLZAv0tb1g==",
"requires": {
"@babel/runtime": "^7.8.3",
"@wordpress/a11y": "^2.7.0",
"@wordpress/blob": "^2.7.0",
"@wordpress/blocks": "^6.12.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/compose": "^3.11.0",
"@wordpress/data": "^4.14.0",
"@wordpress/deprecated": "^2.7.0",
@ -4241,25 +4454,25 @@
}
},
"@wordpress/block-library": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/@wordpress/block-library/-/block-library-2.14.0.tgz",
"integrity": "sha512-LAKUGnlrV5bdTRX/QnTItKMlhBIEsMgW3E9PWxYBUhrFIhrcrAu007jW8Q6oqsMNyDeakNPpd2MderTGAAzJpw==",
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/@wordpress/block-library/-/block-library-2.14.1.tgz",
"integrity": "sha512-gq+PKbnnQr/kFm29Cqh5n9EABL1V/Ey79eItfNhzCle92r79kgUwfDjRvoVbzikNU5F1j392ojpZK8EQCn+T8w==",
"requires": {
"@babel/runtime": "^7.8.3",
"@wordpress/a11y": "^2.7.0",
"@wordpress/api-fetch": "^3.11.0",
"@wordpress/autop": "^2.6.0",
"@wordpress/blob": "^2.7.0",
"@wordpress/block-editor": "^3.7.0",
"@wordpress/block-editor": "^3.7.1",
"@wordpress/blocks": "^6.12.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/compose": "^3.11.0",
"@wordpress/core-data": "^2.12.0",
"@wordpress/data": "^4.14.0",
"@wordpress/date": "^3.8.0",
"@wordpress/deprecated": "^2.7.0",
"@wordpress/dom": "^2.8.0",
"@wordpress/editor": "^9.12.0",
"@wordpress/editor": "^9.12.1",
"@wordpress/element": "^2.11.0",
"@wordpress/escape-html": "^1.7.0",
"@wordpress/i18n": "^3.9.0",
@ -4268,7 +4481,7 @@
"@wordpress/keycodes": "^2.9.0",
"@wordpress/primitives": "^1.1.0",
"@wordpress/rich-text": "^3.12.0",
"@wordpress/server-side-render": "^1.8.0",
"@wordpress/server-side-render": "^1.8.1",
"@wordpress/url": "^2.11.0",
"@wordpress/viewport": "^2.13.0",
"classnames": "^2.2.5",
@ -4316,9 +4529,9 @@
}
},
"@wordpress/components": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@wordpress/components/-/components-9.2.0.tgz",
"integrity": "sha512-8KETp5Dafq1uYaQc3D9EWqPG/pM8ffGyqwCGr7n+Gc9v86v7ctIWNz2iJvjY1Pw7Sr/AlezhstOVE5z8sHMZag==",
"version": "9.2.1",
"resolved": "https://registry.npmjs.org/@wordpress/components/-/components-9.2.1.tgz",
"integrity": "sha512-OAfibZccphrOmQyc8PF4Y5b5iAGCrzrySh9tgCP1tgYkzY9RwULmYnAFDtrYjfCF9najPTI6Snvea5z7huwlJg==",
"requires": {
"@babel/runtime": "^7.8.3",
"@emotion/core": "^10.0.22",
@ -4453,28 +4666,28 @@
}
},
"@wordpress/edit-post": {
"version": "3.13.0",
"resolved": "https://registry.npmjs.org/@wordpress/edit-post/-/edit-post-3.13.0.tgz",
"integrity": "sha512-VNZi8QryeoUwN1shjB1G0DSEoFICVkAUsj/gCtC3iG5NVlT2UTRUwrNa45IFZ9uMNSOdDVL5ydAoSOIauqpJ3Q==",
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@wordpress/edit-post/-/edit-post-3.13.1.tgz",
"integrity": "sha512-ypHBgc+tO7N2Syyqxe/aHh2AlJ1qXNQ01IIeeESATpwlrLpAap+QrSbdALri3p9L1mHVWPRVbwdB42usfzshRw==",
"requires": {
"@babel/runtime": "^7.8.3",
"@wordpress/a11y": "^2.7.0",
"@wordpress/api-fetch": "^3.11.0",
"@wordpress/block-editor": "^3.7.0",
"@wordpress/block-library": "^2.14.0",
"@wordpress/block-editor": "^3.7.1",
"@wordpress/block-library": "^2.14.1",
"@wordpress/blocks": "^6.12.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/compose": "^3.11.0",
"@wordpress/core-data": "^2.12.0",
"@wordpress/data": "^4.14.0",
"@wordpress/editor": "^9.12.0",
"@wordpress/editor": "^9.12.1",
"@wordpress/element": "^2.11.0",
"@wordpress/hooks": "^2.7.0",
"@wordpress/i18n": "^3.9.0",
"@wordpress/icons": "^1.1.0",
"@wordpress/keyboard-shortcuts": "^1.1.0",
"@wordpress/keycodes": "^2.9.0",
"@wordpress/media-utils": "^1.7.0",
"@wordpress/media-utils": "^1.7.1",
"@wordpress/notices": "^2.0.0",
"@wordpress/plugins": "^2.12.0",
"@wordpress/url": "^2.11.0",
@ -4487,18 +4700,18 @@
}
},
"@wordpress/editor": {
"version": "9.12.0",
"resolved": "https://registry.npmjs.org/@wordpress/editor/-/editor-9.12.0.tgz",
"integrity": "sha512-4Ub/ksSsMX347kA+dO3lgPqEF7XKgUTuqsuegXiXVqotxczU0FWPumvWtCkcBr5lodBs1OtDgJzQYujQ/Sk3ww==",
"version": "9.12.1",
"resolved": "https://registry.npmjs.org/@wordpress/editor/-/editor-9.12.1.tgz",
"integrity": "sha512-WjwQV+vESD63HBgi7ueBXzPt0enEZBi2SGzrfj/zPI1mLskNZTC0Ep6iKhU+PCLBJC6W/OoYct5PeMd2BjbuVA==",
"requires": {
"@babel/runtime": "^7.8.3",
"@wordpress/api-fetch": "^3.11.0",
"@wordpress/autop": "^2.6.0",
"@wordpress/blob": "^2.7.0",
"@wordpress/block-directory": "^1.5.0",
"@wordpress/block-editor": "^3.7.0",
"@wordpress/block-directory": "^1.5.1",
"@wordpress/block-editor": "^3.7.1",
"@wordpress/blocks": "^6.12.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/compose": "^3.11.0",
"@wordpress/core-data": "^2.12.0",
"@wordpress/data": "^4.14.0",
@ -4513,10 +4726,10 @@
"@wordpress/is-shallow-equal": "^1.8.0",
"@wordpress/keyboard-shortcuts": "^1.1.0",
"@wordpress/keycodes": "^2.9.0",
"@wordpress/media-utils": "^1.7.0",
"@wordpress/media-utils": "^1.7.1",
"@wordpress/notices": "^2.0.0",
"@wordpress/rich-text": "^3.12.0",
"@wordpress/server-side-render": "^1.8.0",
"@wordpress/server-side-render": "^1.8.1",
"@wordpress/url": "^2.11.0",
"@wordpress/viewport": "^2.13.0",
"@wordpress/wordcount": "^2.7.0",
@ -4641,9 +4854,9 @@
}
},
"@wordpress/media-utils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@wordpress/media-utils/-/media-utils-1.7.0.tgz",
"integrity": "sha512-SPfCB1BKC6PZ3BArYXjAf2aQXKeYGVKLL0sVt/k81bNngkfBUE8/cJ/vnNhrIzYPr/ZJX72IgDzOvWzS6fpm2w==",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@wordpress/media-utils/-/media-utils-1.7.1.tgz",
"integrity": "sha512-cfcCAvhrCUMjYjBhbtKO1Y5GqFvxFqosYykkbDifbwj8DA+P8+Fh2Q9aWKieVh8d7RAES3DqFVY7qvINkKiYMw==",
"requires": {
"@babel/runtime": "^7.8.3",
"@wordpress/api-fetch": "^3.11.0",
@ -4725,13 +4938,13 @@
}
},
"@wordpress/server-side-render": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/@wordpress/server-side-render/-/server-side-render-1.8.0.tgz",
"integrity": "sha512-u8aGO5ygmb8fiskev0mXvjNSl1oxHe7czbBZ2MSYWnbdu0VpJdPDkj7eWNSxbpQKCDj1gjNgVPAcarWrK9fdKA==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@wordpress/server-side-render/-/server-side-render-1.8.1.tgz",
"integrity": "sha512-JLLwyY6W/KxALMc72R1KgrtTaiHoyNzCjNI8bVD192z+gFTFdBg8d7F3CUMwaCzkfKxf09Y0zn7955c0S6k3Ew==",
"requires": {
"@babel/runtime": "^7.8.3",
"@wordpress/api-fetch": "^3.11.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/data": "^4.14.0",
"@wordpress/deprecated": "^2.7.0",
"@wordpress/element": "^2.11.0",
@ -5793,6 +6006,25 @@
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"babel-plugin-typescript-to-proptypes": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/babel-plugin-typescript-to-proptypes/-/babel-plugin-typescript-to-proptypes-1.2.1.tgz",
"integrity": "sha512-D44MNR2bEORkudJdntPoi13EaKfzXlDSCg8XZ6YMBrKxSAFFPTcOrYiPizKU+tdkojdM7bbgL6QAghsyO9R3Xg==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.8.3",
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/plugin-syntax-typescript": "^7.8.3"
},
"dependencies": {
"@babel/helper-plugin-utils": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz",
"integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==",
"dev": true
}
}
},
"babel-register": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
@ -7316,9 +7548,9 @@
}
},
"csstype": {
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.8.tgz",
"integrity": "sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA=="
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz",
"integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q=="
},
"currently-unhandled": {
"version": "0.4.1",
@ -16996,6 +17228,15 @@
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
"dev": true
},
"tsutils": {
"version": "3.17.1",
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
"integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
"dev": true,
"requires": {
"tslib": "^1.8.1"
}
},
"tty-browserify": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
@ -17059,6 +17300,12 @@
"is-typedarray": "^1.0.0"
}
},
"typescript": {
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz",
"integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==",
"dev": true
},
"ua-parser-js": {
"version": "0.7.21",
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",

View File

@ -10,24 +10,27 @@
"not dead"
],
"scripts": {
"lint": "npm run lint6 && npm run lint5 && npm run lint-tests",
"lint": "npm run lint6 && npm run lint-ts && npm run lint5 && npm run lint-tests",
"lint6": "eslint -c .eslintrc.es6.json --max-warnings 0 'assets/js/src/**/*.jsx' 'tests/javascript/**/*.js'",
"lint-ts": "eslint -c .eslintrc.ts.json --max-warnings 0 'assets/js/src/**/*.tsx' 'assets/js/src/**/*.ts'",
"lint5": "eslint -c .eslintrc.es5.json --ignore-pattern helpscout.js --max-warnings 0 'assets/js/src/**/*.js'",
"lint-tests": "eslint -c .eslintrc.tests_newsletter_editor.json --max-warnings 0 'tests/javascript_newsletter_editor'",
"autoprefixer": "postcss assets/dist/css/*.css --use autoprefixer --no-map --replace",
"scss": "node-sass assets/css/src/ --output assets/dist/css/ --output-style compact",
"stylelint": "stylelint --fix",
"test": "mocha --require @babel/register tests/javascript/**/*.spec.js"
"test": "mocha --require @babel/register tests/javascript/**/*.spec.js",
"check-types": "tsc --noEmit"
},
"dependencies": {
"@babel/runtime": "^7.8.4",
"@babel/runtime-corejs3": "^7.8.4",
"@wordpress/block-editor": "^3.7.0",
"@wordpress/block-editor": "^3.7.1",
"@wordpress/block-library": "^2.14.1",
"@wordpress/blocks": "^6.12.0",
"@wordpress/components": "^9.2.0",
"@wordpress/components": "^9.2.1",
"@wordpress/data": "^4.14.0",
"@wordpress/edit-post": "^3.13.0",
"@wordpress/editor": "^9.12.0",
"@wordpress/edit-post": "^3.13.1",
"@wordpress/editor": "^9.12.1",
"@wordpress/element": "^2.11.0",
"@wordpress/hooks": "^2.7.0",
"@wordpress/i18n": "^3.9.0",
@ -81,11 +84,16 @@
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"@babel/preset-react": "^7.8.3",
"@babel/preset-typescript": "^7.8.3",
"@babel/register": "^7.8.3",
"@types/react": "^16.9.22",
"@typescript-eslint/eslint-plugin": "^2.21.0",
"@typescript-eslint/parser": "^2.21.0",
"autoprefixer": "^9.7.4",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"babel-plugin-transform-commonjs": "^1.1.4",
"babel-plugin-typescript-to-proptypes": "^1.2.1",
"chai": "^4.2.0",
"chai-jq": "0.0.9",
"clean-webpack-plugin": "^1.0.0",
@ -118,6 +126,7 @@
"stylelint-order": "^4.0.0",
"stylelint-scss": "^3.14.0",
"terser-webpack-plugin": "^1.4.3",
"typescript": "^3.8.2",
"webpack": "^4.41.5",
"webpack-cli": "^3.2.1",
"webpack-manifest-plugin": "^2.0.4"

View File

@ -3,7 +3,7 @@ Contributors: mailpoet, wysija
Tags: email, email marketing, post notification, woocommerce emails, email automation, newsletter, newsletter builder, newsletter subscribers
Requires at least: 4.7
Tested up to: 5.3
Stable tag: 3.46.0
Stable tag: 3.46.2
Requires PHP: 5.6
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -184,6 +184,12 @@ Check our [Knowledge Base](https://kb.mailpoet.com) or contact us through our [s
== Changelog ==
= 3.46.2 - 2020-03-03 =
* Editor: delete block confirmation links are white again.
= 3.46.1 - 2020-02-25 =
* Improved: minor changes and fixes.
= 3.46.0 - 2020-02-17 =
* Added: MailPoet now has a Gutenberg block for subscription forms;
* Improved: preview links now works publicly;

View File

@ -48,7 +48,7 @@ class ThrottlingTest extends \MailPoetTest {
$ip2 = SubscriberIP::create();
$ip2->ip = '127.0.0.1';
$ip2->createdAt = Carbon::now()->subMonths(1)->subSeconds(1);
$ip2->createdAt = Carbon::now()->subDays(30)->subSeconds(1);
$ip2->save();
expect(SubscriberIP::count())->equals(2);

View File

@ -224,7 +224,7 @@ describe('Save', function () {
trackEvent: function () {},
},
'newsletter_editor/App': EditorApplication,
'common/thumbnail.jsx': {
'common/thumbnail.ts': {
fromNewsletter: function () {
return promiseMock;
},

View File

@ -42,15 +42,15 @@ class BlockRendererHelperTest extends \MailPoetUnitTest {
public function testItShouldRenderLabel() {
$block = $this->block;
$label = $this->rendererHelper->renderLabel($block);
expect($label)->equals('<label class="mailpoet_text_label">Input label</label>');
$label = $this->rendererHelper->renderLabel($block, []);
expect($label)->regExp('#<label.*class="mailpoet_text_label".*>Input label</label>#m');
$block['params']['required'] = '1';
$label = $this->rendererHelper->renderLabel($block);
expect($label)->equals('<label class="mailpoet_text_label">Input label <span class="mailpoet_required">*</span></label>');
$label = $this->rendererHelper->renderLabel($block, []);
expect($label)->equals('<label class="mailpoet_text_label" >Input label <span class="mailpoet_required">*</span></label>');
$block['params']['hide_label'] = '1';
$label = $this->rendererHelper->renderLabel($block);
$label = $this->rendererHelper->renderLabel($block, []);
expect($label)->equals('');
}

View File

@ -55,7 +55,7 @@ class CheckboxTest extends \MailPoetUnitTest {
$this->rendererHelperMock->expects($this->once())->method('getFieldName')->willReturn('Field name');
$this->rendererHelperMock->expects($this->once())->method('getInputValidation')->willReturn('validation="1"');
$this->rendererHelperMock->expects($this->once())->method('getFieldValue')->willReturn('1');
$html = $this->checkbox->render($this->block);
$html = $this->checkbox->render($this->block, []);
$checkboxLabel = $this->htmlParser->getElementByXpath($html, "//label[@class='mailpoet_checkbox_label']");
expect($checkboxLabel->nodeValue)->equals(' Checkbox label');
$checkbox = $this->htmlParser->getChildElement($checkboxLabel, 'input');

View File

@ -48,7 +48,7 @@ class DateTest extends \MailPoetUnitTest {
$this->baseMock->expects($this->once())->method('getFieldName')->willReturn('Field name');
$this->baseMock->expects($this->any())->method('getInputValidation')->willReturn(' validation="1" ');
$html = $this->date->render($this->block);
$html = $this->date->render($this->block, []);
$mothsSelect = $this->htmlParser->getElementByXpath($html, "//select", 0);
$yearsSelect = $this->htmlParser->getElementByXpath($html, "//select", 1);
expect($mothsSelect->childNodes->length)->equals(13); // Months + placeholder
@ -73,7 +73,7 @@ class DateTest extends \MailPoetUnitTest {
$block['params']['date_type'] = 'year_month_day';
$block['params']['date_format'] = 'MM/DD/YYYY';
$html = $this->date->render($block);
$html = $this->date->render($block, []);
$mothsSelect = $this->htmlParser->getElementByXpath($html, "//select", 0);
$daysSelect = $this->htmlParser->getElementByXpath($html, "//select", 1);
$yearsSelect = $this->htmlParser->getElementByXpath($html, "//select", 2);

View File

@ -2,6 +2,7 @@
namespace MailPoet\Test\Form\Block;
use MailPoet\Form\Block\BlockRendererHelper;
use MailPoet\Form\Block\Html;
class HtmlTest extends \MailPoetUnitTest {
@ -23,25 +24,25 @@ class HtmlTest extends \MailPoetUnitTest {
public function _before() {
parent::_before();
$this->html = new Html();
$this->html = new Html($this->createMock(BlockRendererHelper::class));
}
public function testItShouldRenderCustomHtml() {
$html = $this->html->render($this->block);
expect($html)->equals("<p class=\"mailpoet_paragraph\">line1<br />\nline2</p>");
$html = $this->html->render($this->block, []);
expect($html)->equals("<p class=\"mailpoet_paragraph\" >line1<br />\nline2</p>");
}
public function testItShouldRenderCustomHtmlWithoutAutomaticBrs() {
$block = $this->block;
$block['params']['nl2br'] = '';
$html = $this->html->render($block);
expect($html)->equals("<p class=\"mailpoet_paragraph\">line1\nline2</p>");
$html = $this->html->render($block, []);
expect($html)->equals("<p class=\"mailpoet_paragraph\" >line1\nline2</p>");
}
public function testItShouldNotEscapeHtml() {
$block = $this->block;
$block['params']['text'] = '<p class="my-p">Hello</p>';
$html = $this->html->render($block);
expect($html)->equals("<p class=\"mailpoet_paragraph\"><p class=\"my-p\">Hello</p></p>");
$html = $this->html->render($block, []);
expect($html)->equals("<p class=\"mailpoet_paragraph\" ><p class=\"my-p\">Hello</p></p>");
}
}

View File

@ -59,7 +59,7 @@ class RadioTest extends \MailPoetUnitTest {
$this->baseMock->expects($this->once())->method('getInputValidation')->willReturn(' validation="1" ');
$this->baseMock->expects($this->once())->method('getFieldValue')->willReturn('Radio 2');
$html = $this->radio->render($this->block);
$html = $this->radio->render($this->block, []);
$radio1 = $this->htmlParser->getElementByXpath($html, "//label[@class='mailpoet_radio_label']", 0);
$radio2 = $this->htmlParser->getElementByXpath($html, "//label[@class='mailpoet_radio_label']", 1);

View File

@ -57,7 +57,7 @@ class SegmentTest extends \MailPoetUnitTest {
$this->rendererHelperMock->expects($this->once())->method('getInputValidation')->willReturn('validation="1"');
$this->rendererHelperMock->expects($this->once())->method('getFieldName')->willReturn('Segments');
$html = $this->segment->render($this->block);
$html = $this->segment->render($this->block, []);
$checkbox1 = $this->htmlParser->getElementByXpath($html, "//label[@class='mailpoet_checkbox_label']", 0);
$checkbox2 = $this->htmlParser->getElementByXpath($html, "//label[@class='mailpoet_checkbox_label']", 1);

View File

@ -56,7 +56,7 @@ class SelectTest extends \MailPoetUnitTest {
],
'is_checked' => false,
'is_disabled' => false,
'is_hidden' => false,
],
],
],
@ -64,7 +64,7 @@ class SelectTest extends \MailPoetUnitTest {
}
public function testItRendersSelectBlock() {
$rendered = $this->selectBlock->render($this->block);
$rendered = $this->selectBlock->render($this->block, []);
expect($rendered)->contains(Subscriber::STATUS_SUBSCRIBED);
expect($rendered)->contains(Subscriber::STATUS_UNSUBSCRIBED);
expect($rendered)->contains(Subscriber::STATUS_BOUNCED);
@ -72,19 +72,19 @@ class SelectTest extends \MailPoetUnitTest {
public function testItRendersSelectedOption() {
$this->block['params']['values'][0]['is_checked'] = true;
$rendered = $this->selectBlock->render($this->block);
$rendered = $this->selectBlock->render($this->block, []);
expect($rendered)->contains('selected="selected"');
}
public function testItRendersDisabledOptions() {
$this->block['params']['values'][2]['is_disabled'] = true;
$rendered = $this->selectBlock->render($this->block);
$rendered = $this->selectBlock->render($this->block, []);
expect($rendered)->contains('disabled="disabled"');
}
public function testItDoesNotRenderHiddenOptions() {
$this->block['params']['values'][2]['is_hidden'] = true;
$rendered = $this->selectBlock->render($this->block);
$rendered = $this->selectBlock->render($this->block, []);
expect($rendered)->notContains(Subscriber::STATUS_BOUNCED);
}
}

View File

@ -49,7 +49,7 @@ class TextTest extends \MailPoetUnitTest {
$this->rendererHelperMock->expects($this->once())->method('renderInputPlaceholder')->willReturn('');
$this->rendererHelperMock->expects($this->once())->method('getInputModifiers')->willReturn(' modifiers="mod" ');
$html = $this->text->render($this->block);
$html = $this->text->render($this->block, []);
$input = $this->htmlParser->getElementByXpath($html, '//input');
$name = $this->htmlParser->getAttribute($input, 'name');
$type = $this->htmlParser->getAttribute($input, 'type');

View File

@ -49,7 +49,7 @@ class TextareaTest extends \MailPoetUnitTest {
$this->rendererHelperMock->expects($this->once())->method('getInputModifiers')->willReturn(' modifiers="mod" ');
$this->rendererHelperMock->expects($this->once())->method('getFieldValue')->willReturn('val');
$html = $this->textarea->render($this->block);
$html = $this->textarea->render($this->block, []);
$textarea = $this->htmlParser->getElementByXpath($html, '//textarea');
$name = $this->htmlParser->getAttribute($textarea, 'name');
$validation = $this->htmlParser->getAttribute($textarea, 'validation');

View File

@ -38,6 +38,7 @@ class DisplayFormInWPContentTest extends \MailPoetUnitTest {
$this->renderer = $this->createMock(Renderer::class);
$this->renderer->expects($this->any())->method('renderStyles')->willReturn('<style></style>');
$this->renderer->expects($this->any())->method('renderHTML')->willReturn('<form></form>');
$this->renderer->expects($this->any())->method('renderFormElementStyles')->willReturn('');
$this->hook = new DisplayFormInWPContent($this->wp, $this->repository, $this->renderer, $this->assetsController, $this->templateRenderer);
}

View File

@ -91,7 +91,7 @@ class RendererTest extends \MailPoetUnitTest {
->method('get')
->with('captcha.type')
->willReturn(Captcha::TYPE_DISABLED);
$html = $this->renderer->renderBlocks(Fixtures::get('simple_form_body'), false);
$html = $this->renderer->renderBlocks(Fixtures::get('simple_form_body'), [], false);
$hpLabel = $this->htmlParser->findByXpath($html, "//label[@class='mailpoet_hp_email_label']");
expect($hpLabel->length)->equals(0);
$recaptcha = $this->htmlParser->findByXpath($html, "//div[@class='mailpoet_recaptcha']");

67
tsconfig.json Normal file
View File

@ -0,0 +1,67 @@
{
"include": [
"assets/js/src/**/*"
],
"compilerOptions": {
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"strict": false, /* Enable all strict type-checking options. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"baseUrl": "./assets/js/src", /* Base directory to resolve non-absolute module names. */
"rootDir": "./assets/js/src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
/* Strict Type-Checking Options */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": false, /* Enable strict null checks. */
"strictFunctionTypes": false, /* Enable strict checking of function types. */
"strictBindCallApply": false, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": false, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": false, /* Parse in strict mode and emit "use strict" for each source file. */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"allowJs": true, /* Allow javascript files to be compiled. */
"checkJs": false, /* Report errors in .js files. */
// "incremental": true, /* Enable incremental compilation */
// "lib": [], /* Specify library files to be included in the compilation. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
}
}

View File

@ -34,6 +34,11 @@
'changesNotSaved': __('Your changes you made may not be saved'),
'form': __('Form'),
'formSettings': _x('Settings', 'A settings section heading'),
'formSettingsStyles': __('Styles'),
'formSettingsStylesBackgroundColor': __('Background Color'),
'formSettingsStylesFontSize': __('Font Size'),
'formSettingsStylesFontColor': __('Font Color'),
'formSettingsStylesFontColorInherit': __('Inherit from theme'),
'customFieldSettings': _x('Custom field settings', 'A settings section heading'),
'customFieldsFormSettings': _x('Form settings', 'A settings section heading'),
'formPlacement': _x('Form Placement', 'A settings section heading'),

View File

@ -15,6 +15,7 @@
action="<%= admin_url('admin-post.php?action=mailpoet_subscription_form') | raw %>"
class="mailpoet_form mailpoet_form_<%= form_type %>"
novalidate
style="<%= form_element_styles %>"/* paragraphs (label + input)
>
<input type="hidden" name="data[form_id]" value="<%= form_id %>" />
<input type="hidden" name="token" value="<%= token %>" />

View File

@ -40,6 +40,7 @@ const baseConfig = {
'node_modules',
'assets/js/src',
],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
alias: {
'handlebars': 'handlebars/dist/handlebars.js',
'backbone.marionette': 'backbone.marionette/lib/backbone.marionette',
@ -65,7 +66,7 @@ const baseConfig = {
module: {
rules: [
{
test: /\.jsx?$/,
test: /\.(j|t)sx?$/,
exclude: /(node_modules|src\/vendor)/,
loader: 'babel-loader',
},