Compare commits

..

1 Commits
4.3.1 ... 4.0.0

Author SHA1 Message Date
f0b779d724 Release 4.0.0 2022-11-15 15:06:53 +01:00
1546 changed files with 4778 additions and 9744 deletions

View File

@ -179,7 +179,7 @@ jobs:
- run:
name: Download additional WP Plugins for tests
command: |
./do download:woo-commerce-zip 7.1.0
./do download:woo-commerce-zip 7.0.1
./do download:woo-commerce-subscriptions-zip 4.6.0
./do download:woo-commerce-memberships-zip 1.23.1
./do download:woo-commerce-blocks-zip 8.8.2
@ -311,8 +311,7 @@ jobs:
parallelism: 20
working_directory: /home/circleci/mailpoet/mailpoet
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: false
image: ubuntu-2204:2022.07.1
parameters:
multisite:
type: integer
@ -323,7 +322,7 @@ jobs:
mysql_command:
type: string
default: ''
mysql_image:
mysql_image_version:
type: string
default: ''
codeception_image_version:
@ -352,7 +351,7 @@ jobs:
default: 0
environment:
MYSQL_COMMAND: << parameters.mysql_command >>
MYSQL_IMAGE: << parameters.mysql_image >>
MYSQL_IMAGE_VERSION: << parameters.mysql_image_version >>
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
WORDPRESS_IMAGE_VERSION: << parameters.wordpress_image_version >>
steps:
@ -412,12 +411,6 @@ jobs:
circleci tests glob "tests/acceptance/**/*Cest.php" | circleci tests split --split-by=timings > tests/acceptance/_groups/circleci_split_group
fi
cat tests/acceptance/_groups/circleci_split_group
- run:
name: Create docker containers for test
# We experienced some failures when creating containers so we do it explicitly with one retry
command: |
cd tests/docker
docker-compose create || docker-compose create
- run:
name: Run acceptance tests
command: |
@ -483,12 +476,9 @@ jobs:
integration_tests:
working_directory: /home/circleci/mailpoet/mailpoet
machine:
image: ubuntu-2204:2022.10.2
docker_layer_caching: false
image: ubuntu-2204:2022.07.1
environment:
CODECEPTION_IMAGE_VERSION: << parameters.codeception_image_version >>
MYSQL_COMMAND: << parameters.mysql_command >>
MYSQL_IMAGE: << parameters.mysql_image >>
parameters:
codeception_image_version:
type: string
@ -511,10 +501,7 @@ jobs:
multisite:
type: integer
default: 0
mysql_command:
type: string
default: ''
mysql_image:
woo_core_version:
type: string
default: ''
steps:
@ -524,6 +511,14 @@ jobs:
name: 'Pull test docker images'
# Pull docker images with 3 retries
command: i='0';while ! docker-compose -f tests/docker/docker-compose.yml pull && ((i < 3)); do sleep 3 && i=$[$i+1]; done
- when:
condition: << parameters.woo_core_version >>
steps:
- run:
name: Download WooCommerce Core
command: |
cd tests/docker
docker-compose run --rm -w /project --entrypoint "./do download:woo-commerce-zip << parameters.woo_core_version >>" --no-deps codeception_integration
- run:
name: 'PHP Integration tests'
command: |
@ -634,7 +629,7 @@ workflows:
- build
- acceptance_tests:
<<: *slack-fail-post-step
name: acceptance_tests_base_and_woo_cot_off
name: acceptance_tests
requires:
- unit_tests
- static_analysis_php8
@ -646,6 +641,7 @@ workflows:
group: woo
enable_cot: 1
enable_cot_sync: 1
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
requires:
- unit_tests
- static_analysis_php8
@ -657,6 +653,17 @@ workflows:
group: woo
enable_cot: 1
enable_cot_sync: 0
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
requires:
- unit_tests
- static_analysis_php8
- qa_js
- qa_php
- acceptance_tests:
<<: *slack-fail-post-step
name: acceptance_tests_woo_cot_off
group: woo
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
requires:
- unit_tests
- static_analysis_php8
@ -666,11 +673,21 @@ workflows:
<<: *slack-fail-post-step
requires:
- build
- integration_tests:
<<: *slack-fail-post-step
group: woo
name: integration_test_woocommerce
requires:
- unit_tests
- static_analysis_php8
- qa_js
- qa_php
- integration_tests:
<<: *slack-fail-post-step
group: woo
enable_cot: 1
enable_cot_sync: 1
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
name: integration_test_woo_cot_sync
requires:
- unit_tests
@ -682,6 +699,7 @@ workflows:
group: woo
enable_cot: 1
enable_cot_sync: 0
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
name: integration_test_woo_cot_no_sync
requires:
- unit_tests
@ -691,6 +709,7 @@ workflows:
- integration_tests:
<<: *slack-fail-post-step
group: woo
woo_core_version: 7.1.0-rc.2 # Temporarily force COT beta version
name: integration_test_woo_cot_off
requires:
- unit_tests
@ -726,13 +745,15 @@ workflows:
<<: *slack-fail-post-step
requires:
- build
- acceptance_tests_base_and_woo_cot_off
- acceptance_tests
- js_tests
- integration_test_woocommerce
- integration_test_base
- integration_test_woo_cot_no_sync
- integration_test_woo_cot_off
- integration_test_woo_cot_sync
- acceptance_tests_woo_cot_sync
- acceptance_tests_woo_cot_off
- acceptance_tests_woo_cot_no_sync
nightly:
@ -762,8 +783,8 @@ workflows:
woo_subscriptions_version: 4.3.0
woo_memberships_version: 1.21.0
woo_blocks_version: 6.8.0
mysql_command: --max_allowed_packet=100M --default-storage-engine=MYISAM
mysql_image: mysql:5.5
mysql_command: --max_allowed_packet=100M
mysql_image_version: 5.7.36
codeception_image_version: 7.4-cli_20220605.0
wordpress_image_version: wp-5.8_php7.3_20221104.1
requires:
@ -788,8 +809,6 @@ workflows:
<<: *slack-fail-post-step
name: integration_oldest
codeception_image_version: 7.2-cli_20220605.0
mysql_command: --max_allowed_packet=100M --default-storage-engine=MYISAM
mysql_image: mysql:5.5
requires:
- build
- build_premium:

View File

@ -126,24 +126,23 @@ You can access this help in your command line running `./do` without parameters.
[Read the article.](https://mailpoet.atlassian.net/wiki/spaces/MAILPOET/pages/629374977/Adding+new+templates+to+the+plugin)
## 🚥 Testing with different PHP versions
## 🚥 Testing with PHP 7.4 or PHP 8.0
To switch the environment to a different PHP version:
To switch the environment to PHP 7.4/8.0:
1. Check https://github.com/mailpoet/mailpoet/tree/trunk/dev for a list of available PHP versions. Each directory starting with `php` corresponds to a available version.
2. Configure the `wordpress` service in `docker-compose.override.yml` to build from the desired PHP version Dockerfile (replace {PHP_VERSION} with the name of the directory that corresponds to the version that you want to use):
1. Configure the `wordpress` service in `docker-compose.override.yml` to build from the php74 Dockerfile:
```yaml
wordpress:
build:
context: .
dockerfile: dev/{PHP_VERSION}/Dockerfile
dockerfile: dev/php74/Dockerfile # OR dev/php80/Dockerfile
```
3. Run `docker-compose build wordpress`.
4. Start the stack with `./do start`.
2. Run `docker-compose build wordpress`.
3. Start the stack with `./do start`.
To switch back to the default PHP version remove what was added in 2) and, run `docker-compose build wordpress` for application container and `docker-compose build test_wordpress` for tests container,
To switch back to PHP 8.1 remove what was added in 1) and, run `docker-compose build wordpress` for application container and `docker-compose build test_wordpress` for tests container,
and start the stack using `./do start`.
## ✅ TODO

View File

@ -1,46 +0,0 @@
FROM php:8.2.0RC6-apache
ARG UID=1000
ARG GID=1000
# additinal extensions
RUN apt-get update \
&& apt-get install -y git zlib1g-dev libzip-dev zip wget gnupg msmtp libpng-dev gettext subversion \
&& \
# Install NodeJS, enable Corepack
curl -sL https://deb.nodesource.com/setup_17.x | bash - && \
apt-get install -y nodejs build-essential && \
corepack enable && \
\
# Install WP-CLI
curl -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && \
chmod +x /usr/local/bin/wp && \
\
# Clean up
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY dev/php.ini /usr/local/etc/php/conf.d/php_user.ini
# msmtp config
RUN printf "account default\nhost smtp\nport 1025" > /etc/msmtprc
# xdebug build an config
ENV XDEBUGINI_PATH=/usr/local/etc/php/conf.d/xdebug.ini
RUN git clone -b "3.2.0RC2" --depth 1 https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug \
&& docker-php-ext-configure xdebug --enable-xdebug-dev \
&& docker-php-ext-install xdebug \
&& mkdir /tmp/debug
COPY dev/xdebug.ini /tmp/xdebug.ini
RUN cat /tmp/xdebug.ini >> $XDEBUGINI_PATH
# php extensions
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install mysqli
# allow .htaccess files (between <Directory /var/www/> and </Directory>, which is WordPress installation)
RUN sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
# ensure existing content in /var/www/html respects UID and GID, give Node permissions for Corepack
RUN chown -R ${UID}:${GID} /var/www/html && \
mkdir -p /.node && chown -R ${UID}:${GID} /.node

View File

@ -15,7 +15,6 @@ services:
volumes:
- my-datavolume:/var/lib/mysql
- ./dev/database/create_test_db.sh:/docker-entrypoint-initdb.d/10-create_test_db.sh
command: --sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,ANSI,ONLY_FULL_GROUP_BY
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress

View File

@ -1,4 +1,4 @@
<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing
<?php
// phpcs:disable PSR1.Classes.ClassDeclaration
// phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
@ -425,9 +425,6 @@ class RoboFile extends \Robo\Tasks {
$collection->addCode(function() {
return $this->qaCodeSniffer([]);
});
$collection->addCode(function() {
return $this->qaMinimalPluginStandard([]);
});
return $collection->run();
}
@ -539,55 +536,6 @@ class RoboFile extends \Robo\Tasks {
$stringFilesToCheck = !empty($filesToCheck) ? implode(' ', $filesToCheck) : '.';
return $this->taskExec($task)
->arg('--ignore=' . implode(',', $ignorePatterns))
->rawArg($stringFilesToCheck)
->run();
}
public function qaMinimalPluginStandard(array $filesToCheck, $opts = ['severity' => 'all']) {
$severityFlag = $opts['severity'] === 'all' ? '-w' : '-n';
$task = implode(' ', [
'php -d memory_limit=-1',
'./tasks/code_sniffer/vendor/bin/phpcs',
'--extensions=php',
$severityFlag,
'--standard=tasks/code_sniffer/vendor/wporg/plugin-directory/MinimalPluginStandard',
'-s',
]);
$ignorePaths = [
'.mp_svn',
'assets',
'doc',
'generated',
'lib/Config/PopulatorData/Templates',
'lib-3rd-party',
'node_modules',
'plugin_repository',
'prefixer/build',
'prefixer/vendor',
'tasks/code_sniffer/vendor',
'tasks/phpstan/vendor',
'tasks/makepot',
'tools/vendor',
'temp',
'tests/_data',
'tests/_output',
'tests/_support/_generated',
'vendor',
'vendor-prefixed',
'views',
];
// the "--ignore" arg takes a list of regexes, we need to anchor and escape them
$ignorePatterns = array_map(function (string $path): string {
return '^' . preg_quote(__DIR__ . DIRECTORY_SEPARATOR . $path);
}, $ignorePaths);
$stringFilesToCheck = !empty($filesToCheck) ? implode(' ', $filesToCheck) : '.';
return $this
->taskExec($task)
->arg('--ignore=' . implode(',', $ignorePatterns))
@ -851,9 +799,6 @@ class RoboFile extends \Robo\Tasks {
->addCode(function () use ($version) {
$this->releaseCreatePullRequest($version);
})
->addCode(function () use ($version) {
$this->releaseRerunCircleWorkflow(\MailPoetTasks\Release\CircleCiController::PROJECT_PREMIUM);
})
->addCode(function () use ($version) {
$this->translationsPrepareLanguagePacks($version);
})
@ -1171,18 +1116,6 @@ class RoboFile extends \Robo\Tasks {
$this->say("Release '$version[name]' info was published on Slack.");
}
public function releaseRerunCircleWorkflow(string $project = null) {
$circleciController = $this->createCircleCiController();
$result = $circleciController->rerunLatestWorkflow($project);
// Sometimes can be useful to know which Circle project workflow was restarted
$project = $project ? " for the project '{$project}'" : '';
if (!$result) {
$this->yell("Circle Workflow{$project} was not restarted", 40, 'red');
} else {
$this->say("Circle Workflow{$project} was started from the beginning");
}
}
public function downloadWooCommerceBlocksZip($tag = null) {
$this->createWpOrgDownloader('woo-gutenberg-products-block')
->downloadPluginZip('woo-gutenberg-products-block.zip', __DIR__ . '/tests/plugins/', $tag);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -36,8 +36,7 @@
input,
select,
textarea,
input[type='text'].components-form-token-field__input {
textarea {
background: right top/26px no-repeat url('../../img/icons/alert.svg');
padding-right: 26px;
}

View File

@ -51,8 +51,4 @@
.mailpoet_form_field_block {
display: block;
}
.mailpoet_form_field_input_nowrap {
white-space: nowrap;
}
}

View File

@ -122,21 +122,3 @@ body .components-modal__screen-overlay {
justify-content: flex-end;
margin-top: $grid-gap-half;
}
.mailpoet-locked-badge {
align-items: center;
background: #fcf9e8;
border: .5px solid #f5e6ab;
border-radius: 4px;
color: #bd8600;
display: flex;
font-size: 11px;
font-weight: 500;
gap: 4px;
height: 20px;
letter-spacing: .2px;
line-height: 16px;
padding: 2px 8px 2px 4px;
text-transform: uppercase;
width: 82px;
}

View File

@ -22,12 +22,6 @@
}
}
#mailpoet-wizard-container {
.mailpoet-top-bar {
left: 0;
}
}
.mailpoet-wizard-logo {
margin-bottom: 100px;
text-align: center;
@ -41,7 +35,6 @@
align-items: center;
display: flex;
justify-content: center;
margin-top: 8px;
@include respond-to(medium-screen) {
flex-direction: column;
@ -49,7 +42,7 @@
}
.mailpoet-wizard-step-illustration {
margin-right: $grid-gap-xl;
margin-right: $grid-gap;
max-width: $grid-column;
text-align: center;
width: 100%;
@ -70,16 +63,12 @@
}
.mailpoet-wizard-step-content {
max-width: 480px;
max-width: $grid-column-small + $grid-gap + $grid-column;
width: 100%;
@include respond-to(medium-screen) {
max-width: $grid-column;
}
.mailpoet-button {
height: 36px;
}
}
.mailpoet-wizard-label {
@ -122,16 +111,12 @@
.mailpoet-wizard-woocommerce-option {
align-items: center;
box-shadow: 0 1px 0 0 $color-tertiary-light;
box-shadow: 0 -1px 0 0 $color-tertiary-light;
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
padding-bottom: 25px;
padding-top: 1px;
&:last-child {
box-shadow: 0 0;
}
}
.mailpoet-wizard-note {

View File

@ -1,12 +0,0 @@
.mailpoet_captcha_form {
.mailpoet_icon_button {
background: transparent;
border: 0;
cursor: pointer;
img {
height: 20px;
width: 20px;
}
}
}

View File

@ -112,11 +112,3 @@
max-width: 100%;
width: 100%;
}
.authorize-sender-email-and-domain-modal {
z-index: 30; // overlay other modals
}
.authorize-sender-email-and-domain-modal-overlay {
z-index: $modal-screen-overlay-z-index + 4; // overlay other modals
}

View File

@ -29,6 +29,7 @@ $beamer-dot-size: 8px;
}
.mailpoet-top-bar-logo {
cursor: pointer;
position: relative;
top: 2px;
z-index: 1;
@ -38,10 +39,6 @@ $beamer-dot-size: 8px;
max-width: 100%;
}
}
a.mailpoet-top-bar-logo {
cursor: pointer;
}
}
.mailpoet-top-bar-logo-desktop {

View File

@ -19,4 +19,3 @@
@import 'components-public/public';
@import 'components-public/animation';
@import 'components-public/form_colors';
@import 'components-public/captcha';

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect x="0" fill="none" width="20" height="20"/><g><path d="M2 7h4l5-4v14l-5-4H2V7zm12.69-2.46C14.82 4.59 18 5.92 18 10s-3.18 5.41-3.31 5.46c-.06.03-.13.04-.19.04-.2 0-.39-.12-.46-.31-.11-.26.02-.55.27-.65.11-.05 2.69-1.15 2.69-4.54 0-3.41-2.66-4.53-2.69-4.54-.25-.1-.38-.39-.27-.65.1-.25.39-.38.65-.27zM16 10c0 2.57-2.23 3.43-2.32 3.47-.06.02-.12.03-.18.03-.2 0-.39-.12-.47-.32-.1-.26.04-.55.29-.65.07-.02 1.68-.67 1.68-2.53s-1.61-2.51-1.68-2.53c-.25-.1-.38-.39-.29-.65.1-.25.39-.39.65-.29.09.04 2.32.9 2.32 3.47z"/></g></svg>

Before

Width:  |  Height:  |  Size: 587 B

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><rect x="0" fill="none" width="20" height="20"/><g><path d="M10.25 1.02c5.1 0 8.75 4.04 8.75 9s-3.65 9-8.75 9c-3.2 0-6.02-1.59-7.68-3.99l2.59-1.52c1.1 1.5 2.86 2.51 4.84 2.51 3.3 0 6-2.79 6-6s-2.7-6-6-6c-1.97 0-3.72 1-4.82 2.49L7 8.02l-6 2v-7L2.89 4.6c1.69-2.17 4.36-3.58 7.36-3.58z"/></g></svg>

Before

Width:  |  Height:  |  Size: 355 B

View File

@ -1,10 +1,12 @@
import { useState, Fragment } from 'react';
import { DropdownMenu } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { moreVertical, trash } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { Hooks } from 'wp-js-hooks';
import { PremiumModal } from 'common/premium_modal';
import { Step as StepData } from './types';
import { storeName } from '../../store';
import { StepMoreControlsType } from '../../../types/filters';
type Props = {
@ -12,6 +14,12 @@ type Props = {
};
export function StepMoreMenu({ step }: Props): JSX.Element {
const { stepType } = useSelect(
(select) => ({
stepType: select(storeName).getStepType(step.key),
}),
[step],
);
const [showModal, setShowModal] = useState(false);
const moreControls: StepMoreControlsType = Hooks.applyFilters(
@ -45,6 +53,7 @@ export function StepMoreMenu({ step }: Props): JSX.Element {
},
},
step,
stepType,
);
const slots = Object.values(moreControls).filter(

View File

@ -21,7 +21,7 @@ const Dropdown: ComponentType<
}
> = WpDropdown;
function DocumentActions({ children }): JSX.Element {
export function DocumentActions({ children }): JSX.Element {
const { automationName, automationStatus, showIconLabels } = useSelect(
(select) => ({
automationName: select(storeName).getAutomationData().name,
@ -101,6 +101,3 @@ function DocumentActions({ children }): JSX.Element {
</div>
);
}
DocumentActions.displayName = 'DocumentActions';
export { DocumentActions };

View File

@ -9,7 +9,6 @@ import {
import { useDispatch, useSelect } from '@wordpress/data';
import { createContext } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { ErrorBoundary } from 'common';
import { Chip } from '../chip';
import { ColoredIcon } from '../icons';
import {
@ -71,8 +70,6 @@ function StepError({ stepId }: StepErrorProps): JSX.Element {
);
}
StepError.displayName = 'StepError';
export function Errors(): JSX.Element | null {
const [showPopover, setShowPopover] = useState(false);
@ -163,11 +160,9 @@ export function Errors(): JSX.Element | null {
__('The following steps are not fully set:', 'mailpoet')
}
</div>
<ErrorBoundary>
{stepErrors.map((error) => (
<StepError key={error.step_id} stepId={error.step_id} />
))}
</ErrorBoundary>
{stepErrors.map((error) => (
<StepError key={error.step_id} stepId={error.step_id} />
))}
</Composite>
</ErrorsCompositeContext.Provider>
</Popover>

View File

@ -8,7 +8,6 @@ import {
import { dispatch, useDispatch, useSelect } from '@wordpress/data';
import { PinnedItems } from '@wordpress/interface';
import { __ } from '@wordpress/i18n';
import { ErrorBoundary } from 'common';
import { DocumentActions } from './document_actions';
import { Errors } from './errors';
import { InserterToggle } from './inserter_toggle';
@ -228,32 +227,28 @@ export function Header({ showInserterToggle }: Props): JSX.Element {
</div>
<div className="edit-site-header_center">
<ErrorBoundary>
<DocumentActions>
{() => (
<div className="mailpoet-automation-editor-dropdown-name-edit">
<div className="mailpoet-automation-editor-dropdown-name-edit-title">
{__('Automation name', 'mailpoet')}
</div>
<TextControl
value={automationName}
onChange={(newName) => setAutomationName(newName)}
help={__(
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
'mailpoet',
)}
/>
<DocumentActions>
{() => (
<div className="mailpoet-automation-editor-dropdown-name-edit">
<div className="mailpoet-automation-editor-dropdown-name-edit-title">
{__('Automation name', 'mailpoet')}
</div>
)}
</DocumentActions>
</ErrorBoundary>
<TextControl
value={automationName}
onChange={(newName) => setAutomationName(newName)}
help={__(
`Give the automation a name that indicates its purpose. E.g. "Abandoned cart recovery"`,
'mailpoet',
)}
/>
</div>
)}
</DocumentActions>
</div>
<div className="edit-site-header_end">
<div className="edit-site-header__actions">
<ErrorBoundary>
<Errors />
</ErrorBoundary>
<Errors />
{automationStatus === AutomationStatus.DRAFT && (
<>
<SaveDraftButton />

View File

@ -1,2 +0,0 @@
export * from './panel';
export * from './form-token-field';

View File

@ -15,7 +15,7 @@ export function DeactivateImmediatelyModal({
return (
<Modal
className="mailpoet-automatoin-deactivate-modal"
title={__('Stop automation for all subscribers?', 'mailpoet')}
title={__('Stop automatoin for all subscribers?', 'mailpoet')}
onRequestClose={onClose}
>
<p>

View File

@ -1,10 +1,7 @@
import { dispatch } from '@wordpress/data';
import { Hooks } from 'wp-js-hooks';
import { storeName } from './constants';
import { StepType } from './types';
export const registerStepType = (stepType: StepType): void => {
dispatch(storeName).registerStepType(
Hooks.applyFilters('mailpoet.automation.register_step_type', stepType),
);
dispatch(storeName).registerStepType(stepType);
};

View File

@ -2,19 +2,11 @@ import { registerStepType } from '../../editor/store';
import { step as SendEmailStep } from './steps/send_email';
import { step as SomeoneSubscribesTrigger } from './steps/someone-subscribes';
import { step as WpUserRegisteredTrigger } from './steps/wp-user-registered';
import { step as AddTagsAction } from './steps/add_tags';
import { step as RemoveTagsAction } from './steps/remove_tags';
import { step as AddToListStep } from './steps/add_to_list';
import { step as RemoveFromListStep } from './steps/remove_from_list';
import { registerStepControls } from './step-controls';
export const initialize = (): void => {
registerStepType(SendEmailStep);
registerStepType(WpUserRegisteredTrigger);
registerStepType(SomeoneSubscribesTrigger);
registerStepType(AddTagsAction);
registerStepType(RemoveTagsAction);
registerStepType(AddToListStep);
registerStepType(RemoveFromListStep);
registerStepControls();
};

View File

@ -2,6 +2,7 @@ import { __ } from '@wordpress/i18n';
import { chartBar } from '@wordpress/icons';
import { Hooks } from 'wp-js-hooks';
import { MoreControlType, StepMoreControlsType } from '../../../types/filters';
import { StepType } from '../../../editor/store';
import { Step } from '../../../editor/components/automation/types';
const emailStatisticsControl = (step: Step): MoreControlType => {
@ -29,8 +30,12 @@ export function registerStepControls() {
Hooks.addFilter(
'mailpoet.automation.step.more-controls',
'mailpoet',
(controls: StepMoreControlsType, step: Step): StepMoreControlsType => {
if (step.key === 'mailpoet:send-email') {
(
controls: StepMoreControlsType,
step: Step,
stepType: StepType,
): StepMoreControlsType => {
if (stepType.key === 'mailpoet:send-email') {
return {
statistics: emailStatisticsControl(step),
...controls,

View File

@ -1,28 +0,0 @@
import { __ } from '@wordpress/i18n';
import { tag } from '@wordpress/icons';
import { StepType } from '../../../../editor/store';
import { LockedBadge } from '../../../../../common/premium_modal/locked_badge';
import { PremiumModalForStepEdit } from '../../../../../common/premium_modal';
export const step: StepType = {
key: 'mailpoet:add-tag',
group: 'actions',
title: __('Add tag', 'mailpoet'),
description: __('Add a tag or multiple tags to a subscriber.', 'mailpoet'),
subtitle: () => <LockedBadge text={__('Premium', 'mailpoet')} />,
foreground: '#00A32A',
background: '#EDFAEF',
icon: () => (
<div style={{ width: '100%', height: '100%', scale: '1.4' }}>{tag}</div>
),
edit: () => (
<PremiumModalForStepEdit
tracking={{
utm_medium: 'upsell_modal',
utm_campaign: 'create_automation_editor_add_tag',
}}
>
{__('Adding tags is a premium feature.', 'mailpoet')}
</PremiumModalForStepEdit>
),
} as const;

View File

@ -1,28 +0,0 @@
import { __ } from '@wordpress/i18n';
import { list } from '@wordpress/icons';
import { StepType } from '../../../../editor/store/types';
import { PremiumModalForStepEdit } from '../../../../../common/premium_modal';
import { LockedBadge } from '../../../../../common/premium_modal/locked_badge';
export const step: StepType = {
key: 'mailpoet:add-to-list',
group: 'actions',
title: __('Add to list', 'mailpoet'),
description: __('Add a subscriber to a list.', 'mailpoet'),
subtitle: () => <LockedBadge text={__('Premium', 'mailpoet')} />,
foreground: '#00A32A',
background: '#EDFAEF',
icon: () => (
<div style={{ width: '100%', height: '100%', scale: '1.12' }}>{list}</div>
),
edit: () => (
<PremiumModalForStepEdit
tracking={{
utm_medium: 'upsell_modal',
utm_campaign: 'create_automation_editor_add_to_list',
}}
>
{__('Adding subscribers to lists is a premium feature.', 'mailpoet')}
</PremiumModalForStepEdit>
),
} as const;

View File

@ -1,28 +0,0 @@
import { __ } from '@wordpress/i18n';
import { list } from '@wordpress/icons';
import { StepType } from '../../../../editor/store/types';
import { PremiumModalForStepEdit } from '../../../../../common/premium_modal';
import { LockedBadge } from '../../../../../common/premium_modal/locked_badge';
export const step: StepType = {
key: 'mailpoet:remove-from-list',
group: 'actions',
title: __('Remove from list', 'mailpoet'),
description: __('Remove a subscriber from a list.', 'mailpoet'),
subtitle: () => <LockedBadge text={__('Premium', 'mailpoet')} />,
foreground: '#00A32A',
background: '#EDFAEF',
icon: () => (
<div style={{ width: '100%', height: '100%', scale: '1.12' }}>{list}</div>
),
edit: () => (
<PremiumModalForStepEdit
tracking={{
utm_medium: 'upsell_modal',
utm_campaign: 'create_automation_editor_remove_from_list',
}}
>
{__('Removing subscribers from lists is a premium feature.', 'mailpoet')}
</PremiumModalForStepEdit>
),
} as const;

View File

@ -1,31 +0,0 @@
import { __ } from '@wordpress/i18n';
import { tag } from '@wordpress/icons';
import { StepType } from '../../../../editor/store';
import { LockedBadge } from '../../../../../common/premium_modal/locked_badge';
import { PremiumModalForStepEdit } from '../../../../../common/premium_modal';
export const step: StepType = {
key: 'mailpoet:remove-tag',
group: 'actions',
title: __('Remove tag', 'mailpoet'),
description: __(
'Remove a tag or multiple tags from a subscriber',
'mailpoet',
),
subtitle: () => <LockedBadge text={__('Premium', 'mailpoet')} />,
foreground: '#00A32A',
background: '#EDFAEF',
icon: () => (
<div style={{ width: '100%', height: '100%', scale: '1.4' }}>{tag}</div>
),
edit: () => (
<PremiumModalForStepEdit
tracking={{
utm_medium: 'upsell_modal',
utm_campaign: 'create_automation_editor_remove_tag',
}}
>
{__('Removing tags is a premium feature.', 'mailpoet')}
</PremiumModalForStepEdit>
),
} as const;

View File

@ -3,10 +3,8 @@ import { dispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { storeName } from '../../../../../editor/store';
import { segments } from './segment';
import {
PlainBodyTitle,
FormTokenField,
} from '../../../../../editor/components';
import { PlainBodyTitle } from '../../../../../editor/components/panel';
import { FormTokenField } from '../../../components/form-token-field';
export function ListPanel(): JSX.Element {
const { selectedStep } = useSelect(

View File

@ -1,4 +1,4 @@
import { FormTokenItem } from '../../../../../editor/components';
import { FormTokenItem } from '../../../components/form-token-field';
type Segment = FormTokenItem & {
type: string;

View File

@ -1,4 +1,4 @@
import { FormTokenItem } from '../../../../../editor/components';
import { FormTokenItem } from '../../../components/form-token-field';
declare global {
interface Window {

View File

@ -3,11 +3,9 @@ import { dispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import ReactStringReplace from 'react-string-replace';
import { storeName } from '../../../../../editor/store';
import {
PlainBodyTitle,
FormTokenField,
} from '../../../../../editor/components';
import { PlainBodyTitle } from '../../../../../editor/components/panel';
import { userRoles } from './role';
import { FormTokenField } from '../../../components/form-token-field';
function SettingsInfoText(): JSX.Element {
return (

View File

@ -92,7 +92,6 @@ function AuthorizeSenderEmailAndDomainModal({
<Modal
onRequestClose={onRequestClose}
contentClassName="authorize-sender-email-and-domain-modal"
overlayClassName="authorize-sender-email-and-domain-modal-overlay"
>
<Tabs activeKey={initialTab}>
<Tab

View File

@ -6,7 +6,7 @@ type Props = {
active: string;
};
function Categories({ onSelect, categories, active }: Props) {
export function Categories({ onSelect, categories, active }: Props) {
const cats = categories.map((category) => (
<CategoriesItem
{...category}
@ -18,6 +18,3 @@ function Categories({ onSelect, categories, active }: Props) {
return <div className="mailpoet-categories">{cats}</div>;
}
Categories.displayName = 'Categories';
export { Categories };

View File

@ -1,7 +1,6 @@
import classnames from 'classnames';
import ReactDatePicker, { ReactDatePickerProps } from 'react-datepicker';
import { MailPoet } from 'mailpoet';
import { withBoundary } from '../error_boundary';
type Props = ReactDatePickerProps & {
dimension?: 'small';
@ -10,7 +9,7 @@ type Props = ReactDatePickerProps & {
iconEnd?: JSX.Element;
};
function Datepicker({
export function Datepicker({
dimension,
isFullWidth,
iconStart,
@ -35,8 +34,3 @@ function Datepicker({
</div>
);
}
Datepicker.displayName = 'Datepicker';
const DatepickerWithBoundary = withBoundary(Datepicker);
export { DatepickerWithBoundary as Datepicker };

View File

@ -1,68 +0,0 @@
import { Children, Component } from '@wordpress/element';
import { ComponentType } from 'react';
import { getComponentDisplayName } from './utils';
type ErrorBoundaryState = {
hasError: boolean;
error?: string;
};
export type ErrorBoundaryProps = {
onError?: (Error_Boundary_State) => ComponentType;
};
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = {
hasError: false,
error: '',
};
}
static getDerivedStateFromError(error: Error & { fileName?: string }) {
return {
hasError: true,
error: `${error?.toString()} \nFile name: ${
error?.fileName ?? 'No fileName reported'
} \nStack trace: ${error?.stack ?? 'No stack trace reported'}`,
};
}
componentDidCatch(error, errorInfo) {
// eslint-disable-next-line no-console
console.log('logging', { error, errorInfo });
}
render() {
const { onError } = this.props;
const { hasError, error } = this.state;
if (hasError) {
if (onError) {
return onError(this.state);
}
return (
<>
<h1>
The application{' '}
<strong>
{Children.map(this.props.children, (child) =>
getComponentDisplayName(child as ComponentType),
).join(', ')}{' '}
</strong>
encountered an error
</h1>
<p>
Please report the following error to{' '}
<a href="https://www.mailpoet.com/support/">MailPoet support</a>
</p>
<pre>{error}</pre>
</>
);
}
return this.props.children;
}
}

View File

@ -1,3 +0,0 @@
export * from './utils';
export * from './error_boundary';
export * from './with_boundary';

View File

@ -1,4 +0,0 @@
import { ComponentType } from 'react';
export const getComponentDisplayName = (component: ComponentType): string =>
component.displayName || component.name || 'Unknown application/component';

View File

@ -1,14 +0,0 @@
import { ComponentType, JSXElementConstructor } from 'react';
import { ErrorBoundary, ErrorBoundaryProps } from './error_boundary';
export const withBoundary = <P extends Record<string, unknown> | unknown>(
Bound: JSXElementConstructor<P>,
props?: ErrorBoundaryProps,
): ComponentType<P> =>
function bounder(boundProps: P) {
return (
<ErrorBoundary {...props}>
<Bound {...boundProps} />
</ErrorBoundary>
);
};

View File

@ -7,7 +7,7 @@ type Props = InputHTMLAttributes<HTMLInputElement> & {
automationId?: string;
};
function Toggle({
export function Toggle({
dimension,
onCheck,
automationId,
@ -33,7 +33,3 @@ function Toggle({
</label>
);
}
Toggle.displayName = 'FormToggle';
export { Toggle };

View File

@ -7,4 +7,3 @@ export * from './loading';
export * from './form';
export * from './tag';
export * from './listings';
export * from './error_boundary';

View File

@ -63,7 +63,7 @@ type NewsletterStatusProps = {
status?: string;
};
function NewsletterStatus({
export function NewsletterStatus({
scheduledFor,
processed,
total,
@ -150,6 +150,3 @@ function NewsletterStatus({
</div>
);
}
NewsletterStatus.displayName = 'NewsletterStatus';
export { NewsletterStatus };

View File

@ -1,9 +1,8 @@
import { useEffect } from 'react';
import { useSelect } from '@wordpress/data';
import { MailPoet } from 'mailpoet';
import { withBoundary } from '../error_boundary';
function UnsavedChangesNotice({ storeName }) {
export function UnsavedChangesNotice({ storeName }) {
const hasUnsavedChanges = useSelect(
(sel) => sel(storeName).hasUnsavedChanges(),
[],
@ -25,7 +24,3 @@ function UnsavedChangesNotice({ storeName }) {
return null;
}
UnsavedChangesNotice.displayName = 'UnsavedChangesNotice';
const UnsavedChangesNoticeWithBoundary = withBoundary(UnsavedChangesNotice);
export { UnsavedChangesNoticeWithBoundary as UnsavedChangesNotice };

View File

@ -13,7 +13,6 @@ import {
Button,
Modal,
} from '@wordpress/components';
import { dispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { MailPoet } from 'mailpoet';
import {
@ -22,8 +21,6 @@ import {
useUpgradeInfo,
UtmParams,
} from './upgrade_info';
import { storeName } from '../../automation/editor/store';
import { withBoundary } from '../error_boundary';
export const premiumValidAndActive =
premiumFeaturesEnabled && MailPoet.premiumActive;
@ -52,7 +49,11 @@ const getCta = (state: State, upgradeInfo: UpgradeInfo): string => {
return cta;
};
function PremiumModal({ children, tracking, ...props }: Props): JSX.Element {
export function PremiumModal({
children,
tracking,
...props
}: Props): JSX.Element {
const [state, setState] = useState<State>();
const upgradeInfo = useUpgradeInfo(tracking);
@ -125,44 +126,3 @@ function PremiumModal({ children, tracking, ...props }: Props): JSX.Element {
</Modal>
);
}
PremiumModal.displayName = 'PremiumModal';
type EditProps = Omit<Props, 'onRequestClose'>;
function PremiumModalForStepEdit({
children,
...props
}: EditProps): JSX.Element {
const [showModal, setShowModal] = useState(true);
useEffect(() => {
if (showModal) {
return;
}
const { selectStep } = dispatch(storeName);
selectStep(undefined);
setShowModal(true);
}, [showModal]);
if (!showModal) {
return null;
}
return (
<PremiumModal
onRequestClose={() => {
setShowModal(false);
}}
{...props}
>
{children}
</PremiumModal>
);
}
PremiumModalForStepEdit.displayName = 'PremiumModalForStepEdit';
const PremiumModalForStepEditWithBoundary = withBoundary(
PremiumModalForStepEdit,
);
export {
PremiumModal,
PremiumModalForStepEditWithBoundary as PremiumModalForStepEdit,
};

View File

@ -1,31 +0,0 @@
const lockIcon = (
<svg
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_1896_34966)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6 1.625C4.96447 1.625 4.125 2.46447 4.125 3.5V5H3.5C3.22386 5 3 5.22386 3 5.5V9.5C3 9.77614 3.22386 10 3.5 10H8.5C8.77614 10 9 9.77614 9 9.5V5.5C9 5.22386 8.77614 5 8.5 5H7.875V3.5C7.875 2.46447 7.03553 1.625 6 1.625ZM7.125 5V3.5C7.125 2.87868 6.62132 2.375 6 2.375C5.37868 2.375 4.875 2.87868 4.875 3.5V5H7.125Z"
fill="#BD8600"
/>
</g>
<defs>
<clipPath id="clip0_1896_34966">
<rect width="12" height="12" fill="white" />
</clipPath>
</defs>
</svg>
);
export function LockedBadge({ text }): JSX.Element {
return (
<span className="mailpoet-locked-badge">
{lockIcon} {text}
</span>
);
}

View File

@ -77,5 +77,5 @@ Preview.defaultProps = {
onDisplayTypeChange: () => {},
selectedDisplayType: 'desktop',
};
Preview.displayName = 'FormEditorPreview';
export { Preview };

View File

@ -1,6 +1,5 @@
import { useEffect } from 'react';
import { withRouter } from 'react-router-dom';
import { withBoundary } from './error_boundary';
function ScrollToTopComponent({ children, location: { pathname } }) {
useEffect(() => {
@ -10,5 +9,4 @@ function ScrollToTopComponent({ children, location: { pathname } }) {
return children || null;
}
ScrollToTopComponent.displayName = 'ScrollToTopComponent';
export const ScrollToTop = withRouter(withBoundary(ScrollToTopComponent));
export const ScrollToTop = withRouter(ScrollToTopComponent);

View File

@ -163,5 +163,5 @@ SenderEmailAddressWarning.defaultProps = {
showSenderDomainWarning: false,
onSuccessfulEmailOrDomainAuthorization: noop,
};
SenderEmailAddressWarning.displayName = 'SenderEmailAddressWarning';
export { SenderEmailAddressWarning };

View File

@ -1,6 +1,5 @@
import range from 'lodash/range';
import classnames from 'classnames';
import { withBoundary } from 'common';
import { ContentWrapperFix } from './content_wrapper_fix';
type Props = {
@ -9,7 +8,7 @@ type Props = {
titles?: string[];
};
function StepsComponent({ count, current, titles }: Props) {
function Steps({ count, current, titles }: Props) {
return (
<div className="mailpoet-steps">
<ContentWrapperFix />
@ -36,9 +35,8 @@ function StepsComponent({ count, current, titles }: Props) {
);
}
StepsComponent.defaultProps = {
Steps.defaultProps = {
titles: [],
};
StepsComponent.displayName = 'StepsComponent';
const Steps = withBoundary(StepsComponent);
export { Steps };

View File

@ -10,7 +10,7 @@ import {
} from 'react-router-dom';
import { noop } from 'lodash';
import { Props as TabProps, Tabs } from './tabs';
import { Tabs, Props as TabProps } from './tabs';
function RouterAwareTabs(
props: TabProps & {
@ -105,6 +105,4 @@ function RoutedTabs({
);
}
RoutedTabs.displayName = 'RoutedTabs';
export { RoutedTabs };

View File

@ -1,5 +1,5 @@
import { Heading } from 'common/typography/heading/heading';
import { SegmentTags, StringTags } from '../tags';
import { Tags } from '../tags';
export default {
title: 'Tags',
@ -18,20 +18,15 @@ export function Segments() {
<>
<Heading level={1}>Tags</Heading>
<Heading level={2}>Segments</Heading>
<SegmentTags segments={segments} dimension="large" />
<Tags segments={segments} dimension="large" />
<div className="mailpoet-gap" />
<SegmentTags segments={segments}>
<Tags segments={segments}>
<span>Prefix: </span>
</SegmentTags>
</Tags>
<Heading level={2}>Strings</Heading>
<StringTags strings={strings} dimension="large" variant="good" />
<Tags strings={strings} dimension="large" variant="good" />
<div className="mailpoet-gap" />
<StringTags
strings={strings}
dimension="large"
variant="good"
isInverted
/>
<Tags strings={strings} dimension="large" variant="good" isInverted />
</>
);
}

View File

@ -3,36 +3,11 @@ import { Tag, TagVariant } from './tag';
import { Tooltip } from '../tooltip/tooltip';
import { MailPoet } from '../../mailpoet';
type SharedTagProps = {
children?: ReactNode;
dimension?: 'large';
variant?: TagVariant;
isInverted?: boolean;
};
type TagData = {
name: string;
target?: string;
tooltip?: string;
};
type TagProps = SharedTagProps & {
tags: TagData[];
};
type StringTagsProps = SharedTagProps & {
strings: string[];
};
type Segment = {
name: string;
id?: string;
};
type SegmentTagsProps = SharedTagProps & {
segments: Segment[];
};
type SubscriberTag = {
id: string;
name: string;
@ -40,96 +15,99 @@ type SubscriberTag = {
tag_id: string;
};
type SubscriberTagsProps = SharedTagProps & {
subscribers: SubscriberTag[];
type Props = {
children?: ReactNode;
dimension?: 'large';
segments?: Segment[];
subscriberTags?: SubscriberTag[];
strings?: string[];
variant?: TagVariant;
isInverted?: boolean;
};
function Tags({ children, tags, dimension, variant, isInverted }: TagProps) {
function Tags({
children,
dimension,
segments,
subscriberTags,
strings,
variant,
isInverted,
}: Props) {
return (
<div className="mailpoet-tags">
{children}
{tags.map((item) => {
const tag = (
<Tag
key={item.name}
dimension={dimension}
variant={variant || 'list'}
isInverted={isInverted}
>
{item.name}
</Tag>
);
if (!item.target) {
return tag;
}
{segments &&
segments.map((segment) => {
const tag = (
<Tag key={segment.name} dimension={dimension} variant="list">
{segment.name}
</Tag>
);
if (!segment.id) {
return tag;
}
const randomId = Math.random().toString(36).substring(2, 15);
const tooltipId = `segment-tooltip-${randomId}`;
const randomId = Math.random().toString(36).substring(2, 15);
const tooltipId = `segment-tooltip-${randomId}`;
return (
<div key={randomId}>
{item.tooltip && (
return (
<div key={randomId}>
<Tooltip id={tooltipId} place="top">
{MailPoet.I18n.t('viewFilteredSubscribersMessage')}
</Tooltip>
)}
<a data-tip="" data-for={tooltipId} href={item.target}>
{tag}
</a>
</div>
);
})}
<a
data-tip=""
data-for={tooltipId}
href={`admin.php?page=mailpoet-subscribers#/filter[segment=${segment.id}]`}
>
{tag}
</a>
</div>
);
})}
{subscriberTags &&
subscriberTags.map((subscriberTag) => {
const randomId = Math.random().toString(36).substring(2, 15);
const tooltipId = `tag-tooltip-${randomId}`;
const tag = (
<Tag
key={subscriberTag.name}
dimension={dimension}
variant={variant || 'list'}
isInverted={isInverted}
>
{subscriberTag.name}
</Tag>
);
return (
<div key={randomId}>
<Tooltip id={tooltipId} place="top">
{MailPoet.I18n.t('viewFilteredSubscribersMessage')}
</Tooltip>
<a
data-tip=""
data-for={tooltipId}
href={`admin.php?page=mailpoet-subscribers#/filter[tag=${subscriberTag.tag_id}]`}
>
{tag}
</a>
</div>
);
})}
{strings &&
strings.map((string) => (
<Tag
key={string}
dimension={dimension}
variant={variant || 'list'} // due to backward compatibility we use `list` as the default value
isInverted={isInverted}
>
{string}
</Tag>
))}
</div>
);
}
Tags.displayName = 'Tags';
function StringTags({ children, strings, ...props }: StringTagsProps) {
const tags: TagData[] = strings.map((item) => ({
name: item,
}));
return (
<Tags tags={tags} {...props}>
{children}
</Tags>
);
}
StringTags.displayName = 'StringTags';
function SegmentTags({ children, segments, ...props }: SegmentTagsProps) {
const tags: TagData[] = segments.map((segment) => ({
name: segment.name,
target: segment.id
? `admin.php?page=mailpoet-subscribers#/filter[segment=${segment.id}]`
: undefined,
tooltip: MailPoet.I18n.t('viewFilteredSubscribersMessage'),
}));
return (
<Tags tags={tags} {...props}>
{children}
</Tags>
);
}
SegmentTags.displayName = 'SegmentTags';
function SubscriberTags({
children,
subscribers,
...props
}: SubscriberTagsProps) {
const tags: TagData[] = subscribers.map((item) => ({
name: item.name,
target: `admin.php?page=mailpoet-subscribers#/filter[tag=${item.tag_id}]`,
tooltip: MailPoet.I18n.t('viewFilteredSubscribersMessage'),
}));
return (
<Tags tags={tags} {...props}>
{children}
</Tags>
);
}
SubscriberTags.displayName = 'SubscriberTags';
export { SegmentTags, StringTags, SubscriberTags };
export { Tags };

View File

@ -5,54 +5,36 @@ import { MailPoetLogoMobile } from './mailpoet_logo_mobile';
type Props = {
onClick?: () => void;
withLink?: boolean;
};
export function MailPoetLogoResponsive({ onClick, withLink = true }: Props) {
export function MailPoetLogoResponsive({ onClick }: Props) {
const history = useHistory();
let logo;
let onLogoClick = onClick;
if (!onClick) {
onLogoClick = () => history.push('/');
}
if (withLink) {
logo = (
<a
role="button"
className="mailpoet-top-bar-logo"
title={t('topBarLogoTitle')}
onClick={onLogoClick}
tabIndex={0}
onKeyDown={(event) => {
if (
['keydown', 'keypress'].includes(event.type) &&
['Enter', ' '].includes(event.key)
) {
event.preventDefault();
onLogoClick();
}
}}
>
<div className="mailpoet-top-bar-logo-desktop">
<MailPoetLogo />
</div>
<div className="mailpoet-top-bar-logo-mobile">
<MailPoetLogoMobile />
</div>
</a>
);
} else {
logo = (
<div className="mailpoet-top-bar-logo">
<div className="mailpoet-top-bar-logo-desktop">
<MailPoetLogo />
</div>
<div className="mailpoet-top-bar-logo-mobile">
<MailPoetLogoMobile />
</div>
return (
<a
role="button"
className="mailpoet-top-bar-logo"
title={t('topBarLogoTitle')}
onClick={onLogoClick}
tabIndex={0}
onKeyDown={(event) => {
if (
['keydown', 'keypress'].includes(event.type) &&
['Enter', ' '].includes(event.key)
) {
event.preventDefault();
onLogoClick();
}
}}
>
<div className="mailpoet-top-bar-logo-desktop">
<MailPoetLogo />
</div>
);
}
return logo;
<div className="mailpoet-top-bar-logo-mobile">
<MailPoetLogoMobile />
</div>
</a>
);
}

View File

@ -5,28 +5,21 @@ import { withFeatureAnnouncement } from 'announcements/with_feature_announcement
import { MailPoetLogoResponsive } from './mailpoet_logo_responsive';
import { BeamerIcon } from './beamer_icon';
import { ScreenOptionsFix } from './screen_options_fix';
import { withBoundary } from '../error_boundary';
type Props = {
children?: ReactNode;
hasNews?: boolean;
onBeamerClick?: () => void;
logoWithLink?: boolean;
};
export function TopBar({
children,
hasNews,
onBeamerClick,
logoWithLink = true,
}: Props) {
export function TopBar({ children, hasNews, onBeamerClick }: Props) {
const buttonClasses = classnames(
'mailpoet-top-bar-beamer',
hasNews ? 'mailpoet-top-bar-beamer-dot' : '',
);
return (
<div className="mailpoet-top-bar">
<MailPoetLogoResponsive withLink={logoWithLink} />
<MailPoetLogoResponsive />
<div className="mailpoet-top-bar-children">{children}</div>
<div className="mailpoet-flex-grow" />
{onBeamerClick && (
@ -58,5 +51,4 @@ export function TopBar({
);
}
TopBar.displayName = 'TopBar';
export const TopBarWithBeamer = withFeatureAnnouncement(withBoundary(TopBar));
export const TopBarWithBeamer = withFeatureAnnouncement(TopBar);

View File

@ -1,9 +1,8 @@
import { useEffect, useState } from 'react';
import { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { MailPoet } from 'mailpoet';
import { GlobalContext, useGlobalContextValue } from 'context/index.jsx';
import { Notices } from 'notices/notices.jsx';
import { ErrorBoundary } from 'common';
function ExperimentalFeatures() {
const [flags, setFlags] = useState(null);
@ -107,14 +106,6 @@ function ExperimentalFeatures() {
const experimentalFeaturesContainer = document.getElementById(
'experimental_features_container',
);
ExperimentalFeatures.displayName = 'ExperimentalFeatures';
if (experimentalFeaturesContainer) {
ReactDOM.render(
<ErrorBoundary>
<ExperimentalFeatures />
</ErrorBoundary>,
experimentalFeaturesContainer,
);
ReactDOM.render(<ExperimentalFeatures />, experimentalFeaturesContainer);
}

View File

@ -1,5 +1,5 @@
import { TokenFieldProps, TokenField } from 'common/form/tokenField/tokenField';
import { FormTokenItem } from '../../automation/editor/components';
import { FormTokenItem } from '../../automation/integrations/mailpoet/components/form-token-field';
interface TokenFormFieldProps {
onValueChange: TokenFieldProps['onChange'];

View File

@ -24,19 +24,8 @@ class FormComponent extends Component {
this.loadItem(this.props.params.id);
} else {
setImmediate(() => {
const defaultValues =
jQuery('.mailpoet_form').mailpoetSerializeObject();
const checkboxField =
Array.isArray(this.props.fields) &&
this.props.fields.length > 0 &&
this.props.fields.find(
(field) => field?.type === 'checkbox' && field?.isChecked,
);
if (checkboxField && checkboxField.name) {
defaultValues[checkboxField.name] = '1';
}
this.setState({
item: defaultValues,
item: jQuery('.mailpoet_form').mailpoetSerializeObject(),
});
});
}

View File

@ -1,6 +1,5 @@
import PanelColorGradientSettings from '@wordpress/block-editor/build-module/components/colors-gradients/panel-color-gradient-settings';
import { useSetting } from '@wordpress/block-editor';
import { withBoundary } from 'common';
type Setting = {
label: string;
@ -15,7 +14,7 @@ type Props = {
settings: Setting[];
};
function ColorGradientSettings({ title, settings }: Props): JSX.Element {
export function ColorGradientSettings({ title, settings }: Props): JSX.Element {
const settingsColors = useSetting('color.palette');
const settingsGradients = useSetting('color.gradients');
return (
@ -29,7 +28,3 @@ function ColorGradientSettings({ title, settings }: Props): JSX.Element {
</div>
);
}
ColorGradientSettings.displayName = 'ColorGradientSettings';
const ColorGradientSettingsWithBoundary = withBoundary(ColorGradientSettings);
export { ColorGradientSettingsWithBoundary as ColorGradientSettings };

View File

@ -1,6 +1,6 @@
import classnames from 'classnames';
import '@wordpress/core-data';
import { useDispatch, useSelect } from '@wordpress/data';
import { useSelect, useDispatch } from '@wordpress/data';
import { Popover, SlotFillProvider } from '@wordpress/components';
import { uploadMedia } from '@wordpress/media-utils';
import {
@ -9,14 +9,13 @@ import {
BlockList,
BlockSelectionClearer,
BlockTools,
WritingFlow,
ObserveTyping,
SETTINGS_DEFAULTS,
WritingFlow,
} from '@wordpress/block-editor';
import { ShortcutProvider } from '@wordpress/keyboard-shortcuts';
import { UnsavedChangesNotice } from 'common/notices/unsaved_changes_notice.jsx';
import { ErrorBoundary } from 'common';
import { fetchLinkSuggestions } from '../utils/link_suggestions';
import { Header } from './header.jsx';
import { Tutorial } from './tutorial';
@ -114,12 +113,10 @@ export function Editor() {
<div className={layoutClass}>
<div className="interface-interface-skeleton__editor">
<div className="interface-interface-skeleton__header">
<ErrorBoundary>
<Header
isInserterOpened={isInserterOpened}
setIsInserterOpened={toggleInserter}
/>
</ErrorBoundary>
<Header
isInserterOpened={isInserterOpened}
setIsInserterOpened={toggleInserter}
/>
</div>
<div className="interface-interface-skeleton__body">
<BlockEditorProvider
@ -135,9 +132,7 @@ export function Editor() {
</div>
)}
<div className="interface-interface-skeleton__content">
<ErrorBoundary>
<Notices />
</ErrorBoundary>
<Notices />
<UnsavedChangesNotice storeName="mailpoet-form-editor" />
<BlockSelectionClearer className="edit-post-visual-editor editor-styles-wrapper">
<BlockTools>
@ -146,11 +141,9 @@ export function Editor() {
<div className="mailpoet_form">
<WritingFlow>
<ObserveTyping>
<ErrorBoundary>
<FormStylingBackground>
<BlockList />
</FormStylingBackground>
</ErrorBoundary>
<FormStylingBackground>
<BlockList />
</FormStylingBackground>
</ObserveTyping>
</WritingFlow>
</div>
@ -159,27 +152,19 @@ export function Editor() {
</div>
{sidebarOpened && (
<div className="interface-interface-skeleton__sidebar">
<ErrorBoundary>
<Sidebar />
</ErrorBoundary>
<Sidebar />
</div>
)}
</BlockEditorProvider>
</div>
<ErrorBoundary>
<FormStyles />
</ErrorBoundary>
<ErrorBoundary>
<Fullscreen />
</ErrorBoundary>
<FormStyles />
<Fullscreen />
</div>
<Popover.Slot />
</div>
</SlotFillProvider>
</ShortcutProvider>
<ErrorBoundary>
<FormPreview />
</ErrorBoundary>
<FormPreview />
<Tutorial />
</>
);

View File

@ -3,9 +3,9 @@ import {
Panel,
PanelBody,
RadioControl,
SelectControl,
TextareaControl,
ToggleControl,
SelectControl,
} from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { MailPoet } from 'mailpoet';
@ -168,5 +168,5 @@ BasicSettingsPanel.propTypes = {
onToggle: PropTypes.func.isRequired,
isOpened: PropTypes.bool.isRequired,
};
BasicSettingsPanel.displayName = 'FormEditorBasicSettingsPanel';
export { BasicSettingsPanel };

View File

@ -1,4 +1,3 @@
import { ErrorBoundary } from 'common';
import { BelowPostsSettings } from './settings_panels/below_posts_settings';
import { PopUpSettings } from './settings_panels/popup_settings';
import { OtherSettings } from './settings_panels/other_settings';
@ -12,31 +11,11 @@ type Props = {
export function SettingsPanel({ activePanel }: Props): JSX.Element {
return (
<div className="mailpoet-styles-settings">
{activePanel === 'others' && (
<ErrorBoundary>
<OtherSettings />
</ErrorBoundary>
)}
{activePanel === 'below_posts' && (
<ErrorBoundary>
<BelowPostsSettings />
</ErrorBoundary>
)}
{activePanel === 'fixed_bar' && (
<ErrorBoundary>
<FixedBarSettings />
</ErrorBoundary>
)}
{activePanel === 'popup' && (
<ErrorBoundary>
<PopUpSettings />
</ErrorBoundary>
)}
{activePanel === 'slide_in' && (
<ErrorBoundary>
<SlideInSettings />
</ErrorBoundary>
)}
{activePanel === 'others' && <OtherSettings />}
{activePanel === 'below_posts' && <BelowPostsSettings />}
{activePanel === 'fixed_bar' && <FixedBarSettings />}
{activePanel === 'popup' && <PopUpSettings />}
{activePanel === 'slide_in' && <SlideInSettings />}
</div>
);
}

View File

@ -2,13 +2,14 @@ import { MailPoet } from 'mailpoet';
import { __, assocPath, compose } from 'lodash/fp';
import { useDispatch, useSelect } from '@wordpress/data';
import { SelectControl } from '@wordpress/components';
import { withBoundary } from 'common';
type Props = {
settingsPlacementKey: string;
};
function AnimationSettings({ settingsPlacementKey }: Props): JSX.Element {
export function AnimationSettings({
settingsPlacementKey,
}: Props): JSX.Element {
const formSettings = useSelect(
(select) => select('mailpoet-form-editor').getFormSettings(),
[],
@ -41,7 +42,3 @@ function AnimationSettings({ settingsPlacementKey }: Props): JSX.Element {
/>
);
}
AnimationSettings.displayName = 'FormEditorAnimationSettings';
const AnimationSettingsWithBoundary = withBoundary(AnimationSettings);
export { AnimationSettingsWithBoundary as AnimationSettings };

View File

@ -2,13 +2,12 @@ import { MailPoet } from 'mailpoet';
import { __, assocPath, compose } from 'lodash/fp';
import { useDispatch, useSelect } from '@wordpress/data';
import { SelectControl } from '@wordpress/components';
import { withBoundary } from 'common';
type Props = {
settingsPlacementKey: string;
};
function CookieSettings({ settingsPlacementKey }: Props): JSX.Element {
export function CookieSettings({ settingsPlacementKey }: Props): JSX.Element {
const cookieExpirationValues = [3, 7, 14, 30, 60, 90];
const formSettings = useSelect(
(select) => select('mailpoet-form-editor').getFormSettings(),
@ -48,7 +47,3 @@ function CookieSettings({ settingsPlacementKey }: Props): JSX.Element {
/>
);
}
CookieSettings.displayName = 'FormEditorCookieSettings';
const CookieSettingsWithBoundary = withBoundary(CookieSettings);
export { CookieSettingsWithBoundary as CookieSettings };

Some files were not shown because too many files have changed in this diff Show More