Compare commits

..

1 Commits
4.6.1 ... 4.0.0

Author SHA1 Message Date
f0b779d724 Release 4.0.0 2022-11-15 15:06:53 +01:00
1712 changed files with 6489 additions and 18285 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,12 +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
- 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
- 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: |
@ -640,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
@ -652,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
@ -663,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
@ -672,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
@ -688,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
@ -697,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
@ -732,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:
@ -768,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:
@ -794,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
@ -297,29 +297,6 @@ class RoboFile extends \Robo\Tasks {
return $this->runTestsInContainer($opts);
}
public function testPerformance($path = null, $opts = ['url' => null, 'head' => false, 'scenario' => null]) {
// run WordPress setup
$this->taskExec('COMPOSE_HTTP_TIMEOUT=200 docker-compose run --rm -it setup')
->dir(__DIR__ . '/tests/performance')
->run();
// run performance tests
$dir = __DIR__;
return $this->taskExec("php $dir/tools/xk6browser.php")
->arg('run')
->option('env', 'URL=' . $opts['url'])
->option('env', 'HEADLESS=' . ($opts['head'] ? 'false' : 'true'))
->option('env', 'SCENARIO=' . $opts['scenario'])
->arg($path ?? "$dir/tests/performance/scenarios.js")
->dir($dir)->run();
}
public function testPerformanceClean() {
$this->taskExec('COMPOSE_HTTP_TIMEOUT=200 docker-compose down --remove-orphans -v')
->dir(__DIR__ . '/tests/performance')
->run();
}
public function testAcceptanceMultisite($opts = ['file' => null, 'skip-deps' => false, 'group' => null, 'timeout' => null, 'enable-cot' => false, 'enable-cot-sync' => false]) {
return $this->runTestsInContainer(array_merge($opts, ['multisite' => true]));
}
@ -448,9 +425,6 @@ class RoboFile extends \Robo\Tasks {
$collection->addCode(function() {
return $this->qaCodeSniffer([]);
});
$collection->addCode(function() {
return $this->qaMinimalPluginStandard([]);
});
return $collection->run();
}
@ -527,56 +501,7 @@ class RoboFile extends \Robo\Tasks {
'./tasks/code_sniffer/vendor/bin/phpcs',
'--extensions=php',
$severityFlag,
'--standard=tasks/code_sniffer/MailPoet/free-ruleset.xml',
'-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))
->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',
'--standard=tasks/code_sniffer/MailPoet',
'-s',
]);
@ -874,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);
})
@ -1194,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

@ -34,10 +34,9 @@
.mailpoet-automation-field__error {
position: relative;
input:not([type='radio'])
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

@ -5,10 +5,6 @@
.components-panel__body-title.mailpoet-automation-panel-plain-body-title {
display: grid;
grid-template-columns: 1fr auto;
&:hover {
background: none;
}
}
.mailpoet-automation-panel-plain-body-title-text {

View File

@ -23,11 +23,3 @@
transform: scale(-1, -1);
transform-origin: center 12.5px;
}
.mailpoet-option-button-opener.is-busy {
animation: components-button__busy-animation 2500ms infinite linear;
background-color: var(--wp-admin-theme-color);
background-image: linear-gradient(-45deg, var(--wp-admin-theme-color) 33%, var(--wp-admin-theme-color-darker-20) 33%, var(--wp-admin-theme-color-darker-20) 70%, var(--wp-admin-theme-color) 70%);
background-size: 100px 100%;
border-color: var(--wp-admin-theme-color);
}

View File

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

View File

@ -1,41 +0,0 @@
.mailpoet_coupon_block {
.mailpoet_editor_coupon {
text-align: center;
}
.mailpoet_editor_coupon_overlay {
background: rgba(255, 255, 255, .5);
color: #000;
cursor: pointer;
display: none;
font-size: 18px;
height: 100%;
position: absolute;
text-align: center;
top: 0;
width: 100%;
}
:hover {
.mailpoet_editor_coupon {
opacity: .5;
}
.mailpoet_editor_coupon_overlay {
display: block;
}
}
}
.coupon_amount_wrapper {
position: relative;
}
.amount_percentage_sign {
font-size: 18px;
font-weight: bold;
left: 150px; // .mailpoet_input_medium width
line-height: 30px;
margin-left: 10px;
position: absolute;
}

View File

@ -111,29 +111,10 @@ h2 {
.edit-post-visual-editor {
background-color: $color-white;
padding: 10px 10px 100px;
padding: 10px;
}
// Unify padding o wp-block-columns with background with front end rendering
.wp-block-columns.has-background {
padding: 10px;
}
// Close button animation
.edit-post-header-toolbar.edit-post-header-toolbar__left > .edit-post-header-toolbar__inserter-toggle {
svg {
transition: transform cubic-bezier(.165, .84, .44, 1) .2s;
}
&.is-pressed svg {
transform: rotate(45deg);
}
}
// Hide block selector header with close button on desktops
@include respond-to(not-small-screen) {
.edit-post-editor__inserter-panel-header {
display: none;
}
}

View File

@ -0,0 +1,28 @@
// Override CSS for HelpScout beacon on form editor page
.admin_page_mailpoet-form-editor {
.BeaconFabButtonFrame,
.BeaconContainer {
left: 175px;
}
&.folded {
.BeaconFabButtonFrame,
.BeaconContainer {
left: 50px;
}
}
@include respond-to(medium-screen) {
.BeaconFabButtonFrame,
.BeaconContainer {
left: 50px;
}
}
@include respond-to(small-screen) {
.BeaconFabButtonFrame,
.BeaconContainer {
left: 15px;
}
}
}

View File

@ -123,5 +123,4 @@
// This style hides the horizontal scrollbar in Firefox browser
.interface-interface-skeleton__sidebar {
overflow-x: hidden;
padding-bottom: 100px;
}

View File

@ -1,26 +0,0 @@
.mailpoet-homepage-section__container {
background-color: $color-white;
box-shadow: rgb(0 0 0 / 10%) 0 0 0 1px;
margin: $grid-gap-xl 0 $grid-gap-medium;
}
.mailpoet-homepage-section__heading {
align-items: center;
border-bottom: 1px solid $color-homepage-borders;
display: grid;
grid-template-columns: auto $grid-gap-medium;
height: 60px;
padding: 0 $grid-gap-medium;
position: relative;
h2 {
font-size: 20px;
font-weight: normal;
line-height: 28px;
margin: 0;
}
@include respond-to(small-screen) {
padding: $grid-gap-half $grid-gap-medium;
}
}

View File

@ -1,5 +0,0 @@
.mailpoet-homepage__container {
margin: 0 auto;
margin-top: $grid-gap-xl;
max-width: 680px;
}

View File

@ -1,68 +0,0 @@
.mailpoet-homepage-product-discovery {
ul {
margin: 0;
}
}
.mailpoet-product-discovery__task {
align-items: center;
border-bottom: 1px solid $color-homepage-borders;
box-sizing: border-box;
cursor: pointer;
display: grid;
font-weight: 600;
grid-template-columns: (124px + $grid-gap-medium) auto $grid-gap-xl;
margin: 0;
padding: $grid-gap-medium;
position: relative;
width: 100%;
@include respond-to(small-screen) {
grid-template-columns: auto $grid-gap-xl;
img {
display: none;
}
}
&:last-child {
border-bottom: none;
}
&:hover {
box-shadow: inset 5px 0 0 0 var(--wp-admin-theme-color);
}
}
.mailpoet-product-discovery__task--completed {
cursor: inherit;
&:hover {
box-shadow: none;
}
}
.mailpoet-product-discovery__task-content {
h3 {
color: var(--wp-admin-theme-color);
font-size: 14px;
line-height: 20px;
margin: 0;
}
p {
color: $color-text-light;
font-weight: normal;
margin: 3px 0 0;
}
}
.mailpoet-product-discovery__task-after {
.mailpoet-task-list__task-icon {
background-color: var(--wp-admin-theme-color);
svg {
fill: $color-white;
}
}
}

View File

@ -1,126 +0,0 @@
$task-icon-size: 32px;
.mailpoet-task-list__task {
background-color: $color-white;
border-bottom: 1px solid $color-homepage-borders;
box-sizing: border-box;
cursor: pointer;
display: grid;
font-weight: 600;
grid-template-columns: $grid-gap-xl auto $grid-gap-xl;
margin: 0;
padding: $grid-gap $grid-gap-medium;
position: relative;
width: 100%;
&:hover {
background-color: $color-grey-0;
}
}
.mailpoet-task-list__task-content {
align-content: center;
color: var(--wp-admin-theme-color);
display: flex;
flex-direction: column;
font-size: 14px;
justify-content: center;
line-height: 20px;
p {
color: $color-text-light;
font-weight: normal;
margin: 3px 0 0;
}
a {
color: var(--wp-admin-theme-color);
&:hover {
color: var(--wp-admin-theme-color-darker-20);
}
}
}
.mailpoet-task-list__task-before {
color: var(--wp-admin-theme-color);
display: flex;
flex-direction: column;
justify-content: center;
}
.mailpoet-task-list__task-icon {
border: 1px solid var(--wp-admin-theme-color);
border-radius: 50%;
box-sizing: border-box;
height: $task-icon-size;
line-height: $task-icon-size;
text-align: center;
width: $task-icon-size;
svg {
fill: var(--wp-admin-theme-color);
margin: 3px;
}
}
.mailpoet-task-list__task--completed {
cursor: inherit;
&:hover {
background-color: $color-white;
}
.mailpoet-task-list__task-icon {
background-color: var(--wp-admin-theme-color);
svg {
fill: $color-white;
}
}
}
.mailpoet-task-list__task--active {
box-shadow: inset 5px 0 0 0 var(--wp-admin-theme-color);
&:after {
background-color: var(--wp-admin-theme-color);
content: '';
height: 100%;
left: 0;
opacity: .1;
pointer-events: none;
position: absolute;
top: 0;
width: 100%;
}
}
.mailpoet-task-list__heading p,
.mailpoet-task-list__all-set {
color: $color-text-light;
font-size: $heading-font-size-h4;
line-height: 1.5;
margin-top: $grid-gap-large;
text-align: center;
}
.mailpoet-task-list__heading {
margin-bottom: $grid-gap-large;
position: relative;
h1 {
margin: 0;
}
p {
margin-top: $grid-gap-half;
text-align: left;
}
.components-dropdown {
position: absolute;
right: 0;
top: 6px;
}
}

View File

@ -56,7 +56,6 @@ p.sender_email_address_warning:first-child {
// Fix for select 2 placeholder padding rendering issue in Chrome
.select2-container .select2-search--inline,
.select2-container .select2-search--inline .select2-search__field {
height: 22px;
max-width: 100%;
}
@ -123,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

@ -27,6 +27,10 @@ input.select2-search__field:-ms-input-placeholder {
color: $color-placeholder-select2;
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border: 1px solid #aaa; /* default Select2 border for single dropdown */
}
textarea.regular-text {
width: 25em !important;
}

View File

@ -1,202 +0,0 @@
#mailpoet_landingpage_container {
$content-padding: 32px 65px;
$mobile-content-padding: 25px;
$landingpage-max-width: 1460px;
.mailpoet-content-center {
text-align: center;
}
.mailpoet-content-padding {
padding: $content-padding;
@include respond-to(small-screen) {
padding: $mobile-content-padding
}
}
.landing-header {
padding: $content-padding;
@include respond-to(small-screen) {
padding: $mobile-content-padding
}
}
.landing-footer {
background-color: $color-landingpage-background-light;
padding: $content-padding;
.landing-footer-content {
box-shadow: 0 -1px 0 0 $color-tertiary-light;
padding: 25px 0;
}
}
.landing-faq {
background-color: $color-landingpage-background-light;
padding: $content-padding;
.mailpoet-faq-accordion {
margin: 25px 0;
}
.landing-faq-mobile {
display: none;
}
@include respond-to(small-screen) {
padding: $mobile-content-padding;
.landing-faq-header {
display: none;
}
.landing-faq-mobile {
display: block;
}
}
}
.landing-content {
.hero-section {
$hero-image-offset: 6rem;
background-color: $color-landingpage-background-light;
margin-top: $hero-image-offset;
padding: $content-padding;
.hero-image {
margin-top: -($hero-image-offset + 2rem);
}
@include respond-to(small-screen) {
padding: $mobile-content-padding
}
}
.landingpage-images {
@include respond-to(medium-screen) {
width: 100%;
}
}
.landingpage-general-features {
p:last-child {
margin: 10px auto;
width: 60%;
}
.landingpage-feature-icon {
display: block;
margin: 0 auto;
padding: 20px;
text-align: center;
}
@include respond-to(medium-screen) {
p:last-child {
width: 100%;
}
}
}
.landingpage-wooCommerce-features {
margin-top: 30px;
padding: 2rem 10rem;
.landingpage-wooCommerce-feature-item {
padding: 30px;
@media screen and (min-width: 960px) and (max-width: 1460px) {
.landingpage-images {
width: 100%;
}
}
div:last-child {
margin: auto;
}
}
@include respond-to(small-screen) {
padding: $mobile-content-padding;
.landingpage-wooCommerce-feature-item {
padding: 25px 0;
div:last-child {
text-align: center;
}
}
}
}
}
main {
margin: 0 auto;
max-width: $landingpage-max-width;
}
}
.mailpoet-faq-accordion {
details {
overflow: hidden;
&:not(:first-child) {
border-top: 1px solid $color-editor-border-structure;
}
summary {
cursor: pointer;
padding: 20px 5px;
position: relative;
&::-webkit-details-marker { // remove default marker
content: '';
display: none;
}
&::marker { // remove default marker
content: '';
display: none;
}
&:after {
content: '';
font-size: 30px;
position: absolute;
right: 20px;
top: 0;
transform: rotate(90deg);
transform-origin: center;
transition: .2s transform ease;
}
@include respond-to(small-screen) {
&:after {
right: -1px;
}
}
}
.content {
max-height: 0;
overflow: hidden;
padding: 10px 5px;
transition: max-height .3s ease;
}
// when accordion is opened
&[open] {
summary:after {
transform: rotate(-90deg);
transition: .5s transform ease;
}
.content {
max-height: 400px;
transition: max-height .5s ease-in;
}
}
}
}

View File

@ -4,6 +4,6 @@
}
// Fix for 3rd party plugins icons in menu that might display broken because we block loading 3rd party CSS on mailepoet pages
#adminmenu :not(.toplevel_page_site-card) .wp-menu-image img {
#adminmenu .wp-menu-image img {
max-width: 20px;
}

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

@ -50,11 +50,7 @@
border: 0 !important;
height: auto !important;
outline: none;
padding: 5px 5px 2px 16px !important;
&.select2-selection--multiple {
padding: 7px 5px 0 16px !important;
}
padding: 10px 5px 5px 16px !important;
}
.select2-selection__arrow {
@ -68,6 +64,10 @@
vertical-align: top;
}
.select2-selection--multiple .select2-selection__rendered {
padding-bottom: 0 !important;
}
.select2-selection__choice {
background: $color-tertiary-light !important;
border: 0 !important;
@ -98,7 +98,7 @@
.select2-search--inline {
display: inline-block;
margin-bottom: 5px;
margin-bottom: 9px;
}
.select2-search__field {

View File

@ -53,7 +53,6 @@
@import 'components-editor/content-blocks/unknown_block_fallback';
@import 'components-editor/content-blocks/woocommerce-heading';
@import 'components-editor/content-blocks/woocommerce-content';
@import 'components-editor/content-blocks/coupon';
// Utilities
// Helpers and overrides.

View File

@ -34,6 +34,7 @@
@import './components-form-editor/custom-field';
@import './components-form-editor/form-title';
@import './components-form-editor/header';
@import './components-form-editor/helpscout';
@import './components-form-editor/form-placement';
@import './components-form-editor/preview';
@import './components-form-editor/block-editor';

View File

@ -1,23 +0,0 @@
@import '../../../node_modules/@woocommerce/components/build-style/style'; // Needed to load WP admin themes color variables
// Settings
// Global variables, config switches. Not producing any CSS.
@import 'settings/breakpoints';
@import 'settings/colors';
@import 'settings/grid';
@import 'settings/typography';
// Tools
// Default mixins and functions. Still not producing any CSS.
@import 'mixins/breakpoints';
// Components
// Actual UI components.
@import 'components-homepage/_layout';
@import 'components-homepage/_task-list';
@import 'components-homepage/_content-section';
@import 'components-homepage/_product-discovery';

View File

@ -85,4 +85,3 @@
@import 'components-plugin/set-from-address-modal';
@import 'components-plugin/stats';
@import 'components-plugin/import-export';
@import 'components-plugin/landingpage';

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,3 +1,3 @@
// WordPress breakpoints
$mailpoet-breakpoint-small: 781px;
$mailpoet-breakpoint-small: 782px;
$mailpoet-breakpoint-medium: 960px;

View File

@ -79,9 +79,3 @@ $color-chip-success-bg: #b8e6bf;
$color-chip-success-text: #005c12;
$color-chip-danger-bg: #facfd2;
$color-chip-danger-text: #8a2424;
// Landing page
$color-landingpage-background-light: #fbfbfb;
// Homepage
$color-homepage-borders: #e0e0e0;

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

@ -18,7 +18,7 @@ import _ from 'underscore';
*/
var eventsCache = [];
function track(name, data = [], options = {}, callback = null) {
function track(name, data = []) {
let trackedData = data;
if (typeof window.mixpanel.track !== 'function') {
@ -33,7 +33,7 @@ function track(name, data = [], options = {}, callback = null) {
trackedData['MailPoet Premium version'] = window.mailpoet_premium_version;
}
window.mixpanel.track(name, trackedData, options, callback);
window.mixpanel.track(name, trackedData);
}
function exportMixpanel() {
@ -45,37 +45,24 @@ function exportMixpanel() {
) {
window.MailPoet.trackEvent = track;
} else {
window.MailPoet.trackEvent = function emptyFunction(
name,
data,
options,
callback,
) {
if (typeof callback === 'function') {
callback();
}
};
window.MailPoet.trackEvent = function emptyFunction() {};
}
}
function trackCachedEvents() {
eventsCache.forEach(function trackIfEnabled(event) {
if (window.mailpoet_analytics_enabled || event.forced) {
track(event.name, event.data, event.options);
track(event.name, event.data);
}
});
}
function cacheEvent(forced, name, data, options, callback) {
function cacheEvent(forced, name, data) {
eventsCache.push({
name: name,
data: data,
options: options,
forced: forced,
});
if (typeof callback === 'function') {
callback();
}
}
function initializeMixpanelWhenLoaded() {

View File

@ -1,4 +1,3 @@
import { Dispatch, SetStateAction, useState } from 'react';
import { Button, DropdownMenu } from '@wordpress/components';
import { chevronDown } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
@ -9,7 +8,7 @@ type OptionButtonPropType = {
variant: Button.ButtonVariant;
controls: StepMoreControlsType;
title: string;
onClick: (setIsBusy: Dispatch<SetStateAction<boolean>>) => void;
onClick: () => void;
};
export function OptionButton({
controls,
@ -17,22 +16,13 @@ export function OptionButton({
onClick,
variant,
}: OptionButtonPropType): JSX.Element {
const [isBusy, setIsBusy] = useState(false);
const slots = Object.values(controls).filter((item) => item.slot);
const dropDownMenuClassNames = isBusy
? `mailpoet-option-button-opener is-busy`
: `mailpoet-option-button-opener`;
return (
<div className="mailpoet-option-button">
<Button
isBusy={isBusy}
disabled={isBusy}
variant={variant}
className="mailpoet-option-button-main"
onClick={() => {
setIsBusy(true);
onClick(setIsBusy);
}}
onClick={onClick}
>
{title}
</Button>
@ -42,20 +32,10 @@ export function OptionButton({
))}
{Object.values(controls).length > 0 && (
<DropdownMenu
className={dropDownMenuClassNames}
className="mailpoet-option-button-opener"
label={__('More', 'mailpoet')}
icon={chevronDown}
controls={Object.values(controls).map((item) => {
const control = {
...item.control,
onClick: () => {
setIsBusy(true);
item.control.onClick(setIsBusy);
},
};
return control;
})}
controls={Object.values(controls).map((item) => item.control)}
popoverProps={{ position: 'bottom left' }}
/>
)}

View File

@ -1,5 +1,4 @@
import { Fragment } from '@wordpress/element';
import { locale } from '../../config';
type Item = {
key: string;
@ -16,7 +15,7 @@ export function Statistics({
items,
labelPosition = 'before',
}: Props): JSX.Element {
const intl = new Intl.NumberFormat(locale.toString());
const intl = new Intl.NumberFormat();
return (
<div className="mailpoet-automation-stats">
{items.map((item, i) => (

View File

@ -4,29 +4,9 @@ declare global {
root: string;
nonce: string;
};
mailpoet_locale_full: string;
mailpoet_automation_count: number;
}
}
export const api = window.mailpoet_automation_api;
export const automationCount = window.mailpoet_automation_count;
// export locale to use with Intl APIs
export const locale: Intl.Locale = (() => {
const tag = (
window.mailpoet_locale_full ??
document.documentElement.lang ??
'en'
).replace('_', '-');
try {
return new Intl.Locale(tag);
} catch (_) {
try {
return new Intl.Locale(tag.split('-')[0]);
} catch (__) {
return new Intl.Locale('en');
}
}
})();

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

@ -45,7 +45,7 @@ export function InserterPopover(): JSX.Element | null {
}
}}
>
<Inserter onInsert={onInsert} showInserterHelpPanel={false} />
<Inserter onInsert={onInsert} />
</Popover>
{showModal && (

View File

@ -20,139 +20,134 @@ const filterItems = (value: string, item: Item[]): Item[] =>
type Props = {
onInsert?: (item: Item) => void;
showInserterHelpPanel?: boolean;
};
export const Inserter = forwardRef(
({ onInsert, showInserterHelpPanel = true }: Props, ref): JSX.Element => {
const [filterValue, setFilterValue] = useState('');
const [hoveredItem, setHoveredItem] = useState(null);
export const Inserter = forwardRef(({ onInsert }: Props, ref): JSX.Element => {
const [filterValue, setFilterValue] = useState('');
const [hoveredItem, setHoveredItem] = useState(null);
const { steps, type } = useSelect(
(select) => ({
steps: select(storeName).getSteps(),
type: select(storeName).getInserterPopover().type,
}),
[],
);
const { steps, type } = useSelect(
(select) => ({
steps: select(storeName).getSteps(),
type: select(storeName).getInserterPopover().type,
}),
[],
);
const groups: Group[] = useMemo(
() =>
type === 'triggers'
? [
{
type: 'triggers',
title: undefined,
// translators: Label for a list of automation steps of type trigger
label: _x('Triggers', 'automation steps', 'mailpoet'),
items: steps.filter(({ group }) => group === 'triggers'),
},
]
: [
{
type: 'actions',
// translators: Label for a list of automation steps of type action
title: _x('Actions', 'automation steps', 'mailpoet'),
// translators: Label for a list of automation steps of type action
label: _x('Actions', 'automation steps', 'mailpoet'),
items: steps.filter(({ group }) => group === 'actions'),
},
{
type: 'logical',
// translators: Label for a list of logical automation steps (if/else, etc.)
title: _x('Logical', 'automation steps', 'mailpoet'),
// translators: Label for a list of logical automation steps (if/else, etc.)
label: _x('Logical', 'automation steps', 'mailpoet'),
items: steps.filter(({ group }) => group === 'logical'),
},
],
[steps, type],
);
const groups: Group[] = useMemo(
() =>
type === 'triggers'
? [
{
type: 'triggers',
title: undefined,
// translators: Label for a list of automation steps of type trigger
label: _x('Triggers', 'automation steps', 'mailpoet'),
items: steps.filter(({ group }) => group === 'triggers'),
},
]
: [
{
type: 'actions',
// translators: Label for a list of automation steps of type action
title: _x('Actions', 'automation steps', 'mailpoet'),
// translators: Label for a list of automation steps of type action
label: _x('Actions', 'automation steps', 'mailpoet'),
items: steps.filter(({ group }) => group === 'actions'),
},
{
type: 'logical',
// translators: Label for a list of logical automation steps (if/else, etc.)
title: _x('Logical', 'automation steps', 'mailpoet'),
// translators: Label for a list of logical automation steps (if/else, etc.)
label: _x('Logical', 'automation steps', 'mailpoet'),
items: steps.filter(({ group }) => group === 'logical'),
},
],
[steps, type],
);
const onHover = useCallback(
(item) => {
setHoveredItem(item);
},
[setHoveredItem],
);
const onHover = useCallback(
(item) => {
setHoveredItem(item);
},
[setHoveredItem],
);
const searchRef = useRef<HTMLInputElement>();
useImperativeHandle(ref, () => ({
focusSearch: () => {
searchRef.current?.focus();
},
}));
const searchRef = useRef<HTMLInputElement>();
useImperativeHandle(ref, () => ({
focusSearch: () => {
searchRef.current?.focus();
},
}));
const filteredGroups = useMemo(
() =>
groups.map((group) => ({
...group,
items: filterItems(filterValue, group.items),
})),
[filterValue, groups],
);
const filteredGroups = useMemo(
() =>
groups.map((group) => ({
...group,
items: filterItems(filterValue, group.items),
})),
[filterValue, groups],
);
return (
<div className="block-editor-inserter__menu">
<div className="block-editor-inserter__main-area">
<div className="block-editor-inserter__content">
<SearchControl
className="block-editor-inserter__search"
onChange={(value: string) => {
if (hoveredItem) setHoveredItem(null);
setFilterValue(value);
}}
value={filterValue}
label={__('Search for automation steps', 'mailpoet')}
placeholder={__('Search', 'mailpoet')}
ref={searchRef}
/>
return (
<div className="block-editor-inserter__menu">
<div className="block-editor-inserter__main-area">
<div className="block-editor-inserter__content">
<SearchControl
className="block-editor-inserter__search"
onChange={(value: string) => {
if (hoveredItem) setHoveredItem(null);
setFilterValue(value);
}}
value={filterValue}
label={__('Search for automation steps', 'mailpoet')}
placeholder={__('Search', 'mailpoet')}
ref={searchRef}
/>
<div className="block-editor-inserter__block-list">
<InserterListbox>
{filteredGroups.map(
(group) =>
group.items.length > 0 && (
<Fragment key={group.type}>
{group.title && (
<div className="block-editor-inserter__panel-header">
<h2 className="block-editor-inserter__panel-title">
<div>{group.title}</div>
</h2>
</div>
)}
<div className="block-editor-inserter__panel-content">
<StepList
items={group.items}
onHover={onHover}
onSelect={(item: Item) => onInsert(item)}
label={group.label}
/>
<div className="block-editor-inserter__block-list">
<InserterListbox>
{filteredGroups.map(
(group) =>
group.items.length > 0 && (
<Fragment key={group.type}>
{group.title && (
<div className="block-editor-inserter__panel-header">
<h2 className="block-editor-inserter__panel-title">
<div>{group.title}</div>
</h2>
</div>
</Fragment>
),
)}
)}
<div className="block-editor-inserter__panel-content">
<StepList
items={group.items}
onHover={onHover}
onSelect={(item: Item) => onInsert(item)}
label={group.label}
/>
</div>
</Fragment>
),
)}
{filteredGroups.reduce(
(sum, { items }) => sum + items.length,
0,
) === 0 && (
<div className="block-editor-inserter__no-results">
<Icon
className="block-editor-inserter__no-results-icon"
icon={blockDefault}
/>
<p>{__('No results found.', 'mailpoet')}</p>
</div>
)}
</InserterListbox>
</div>
{filteredGroups.reduce(
(sum, { items }) => sum + items.length,
0,
) === 0 && (
<div className="block-editor-inserter__no-results">
<Icon
className="block-editor-inserter__no-results-icon"
icon={blockDefault}
/>
<p>{__('No results found.', 'mailpoet')}</p>
</div>
)}
</InserterListbox>
</div>
</div>
{showInserterHelpPanel && hoveredItem && (
<StepInfoPanel item={hoveredItem} />
)}
</div>
);
},
);
{hoveredItem && <StepInfoPanel item={hoveredItem} />}
</div>
);
});

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

@ -3,7 +3,6 @@ import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { storeName } from '../../../store';
import { TrashButton } from '../../actions/trash-button';
import { locale } from '../../../../config';
export function AutomationSidebar(): JSX.Element {
const { automationData } = useSelect(
@ -24,7 +23,7 @@ export function AutomationSidebar(): JSX.Element {
<PanelRow>
<strong>Date added</strong>{' '}
{new Date(Date.parse(automationData.created_at)).toLocaleDateString(
locale.toString(),
undefined,
dateOptions,
)}
</PanelRow>
@ -32,13 +31,13 @@ export function AutomationSidebar(): JSX.Element {
<strong>Activated</strong>{' '}
{automationData.status === 'active' &&
new Date(Date.parse(automationData.updated_at)).toLocaleDateString(
locale.toString(),
undefined,
dateOptions,
)}
{automationData.status !== 'active' &&
automationData.activated_at &&
new Date(Date.parse(automationData.activated_at)).toLocaleDateString(
locale.toString(),
undefined,
dateOptions,
)}
{automationData.status !== 'active' && !automationData.activated_at && (

View File

@ -3,7 +3,6 @@ import { AutomationEditorWindow, State } from './types';
declare let window: AutomationEditorWindow;
export const getInitialState = (): State => ({
registry: { ...window.mailpoet_automation_registry },
context: { ...window.mailpoet_automation_context },
stepTypes: {},
automationData: { ...window.mailpoet_automation },

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,14 +2,7 @@ import { createRegistrySelector } from '@wordpress/data';
import { store as interfaceStore } from '@wordpress/interface';
import { store as preferencesStore } from '@wordpress/preferences';
import { storeName } from './constants';
import {
Registry,
Errors,
Feature,
State,
StepErrors,
StepType,
} from './types';
import { Context, Errors, Feature, State, StepErrors, StepType } from './types';
import { Item } from '../components/inserter/item';
import { Step, Automation } from '../components/automation/types';
@ -32,22 +25,15 @@ export function isActivationPanelOpened(state: State): boolean {
return state.activationPanel.isOpened;
}
export function getRegistry(state: State): Registry {
return state.registry;
export function getContext(state: State): Context {
return state.context;
}
export function getRegistryStep(
export function getContextStep(
state: State,
key: string,
): Registry['steps'][number] | undefined {
return state.registry.steps[key];
}
export function getContext<T = unknown>(
state: State,
key: string,
): T | undefined {
return state.context[key] as T | undefined;
): Context['steps'][number] | undefined {
return state.context.steps[key];
}
export function getSteps(state: State): StepType[] {

View File

@ -2,12 +2,11 @@ import { ComponentType } from 'react';
import { Step, Automation } from '../components/automation/types';
export interface AutomationEditorWindow extends Window {
mailpoet_automation_registry: Registry;
mailpoet_automation_context: Context;
mailpoet_automation: Automation;
}
export type Registry = {
export type Context = {
steps: Record<
string,
{
@ -21,8 +20,6 @@ export type Registry = {
>;
};
export type Context = Record<string, unknown>;
export type StepGroup = 'actions' | 'logical' | 'triggers';
export type StepType = {
@ -49,7 +46,6 @@ export type Errors = {
};
export type State = {
registry: Registry;
context: Context;
stepTypes: Record<string, StepType>;
automationData: Automation;

View File

@ -1,4 +1,3 @@
// exports for extensibility
export { id } from './id';
export * as config from './config';
export * as EditorStore from './editor/store';

View File

@ -19,7 +19,7 @@ export const step: StepType = {
foreground: '#7F54B3',
background: '#f7edf7',
description: __(
'Wait some time before proceeding with the steps below.',
'Wait some time before proceeding with the steps below',
'mailpoet',
),
subtitle: (data): string => {

View File

@ -1,14 +0,0 @@
import { select } from '@wordpress/data';
import { FormTokenItem } from '../../editor/components';
import { storeName } from '../../editor/store';
type Segment = FormTokenItem & {
type: string;
};
export type Context = {
segments?: Segment[];
};
export const getContext = (): Context =>
select(storeName).getContext('mailpoet') as Context;

View File

@ -2,21 +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 { step as UpdateSubscriberStep } from './steps/update-subscriber';
import { registerStepControls } from './step-controls';
export const initialize = (): void => {
registerStepType(SendEmailStep);
registerStepType(WpUserRegisteredTrigger);
registerStepType(SomeoneSubscribesTrigger);
registerStepType(AddTagsAction);
registerStepType(RemoveTagsAction);
registerStepType(AddToListStep);
registerStepType(RemoveFromListStep);
registerStepType(UpdateSubscriberStep);
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

@ -11,9 +11,9 @@ type ReplyToArgs = {
};
export function ReplyToPanel(): JSX.Element {
const { registry, selectedStep, errors } = useSelect(
const { context, selectedStep, errors } = useSelect(
(select) => ({
registry: select(storeName).getRegistry(),
context: select(storeName).getContext(),
selectedStep: select(storeName).getSelectedStep(),
errors: select(storeName).getStepError(
select(storeName).getSelectedStep().id,
@ -30,10 +30,10 @@ export function ReplyToPanel(): JSX.Element {
const prevValue = useRef<{ name?: string; address?: string }>();
// defaults
const argsSchema =
registry.steps['mailpoet:send-email']?.args_schema?.properties ?? {};
const defaultName = argsSchema.reply_to_name?.default;
const defaultAddress = argsSchema.reply_to_address?.default;
const argsContext =
context.steps['mailpoet:send-email']?.args_schema?.properties ?? {};
const defaultName = argsContext.reply_to_name?.default;
const defaultAddress = argsContext.reply_to_address?.default;
const errorFields = errors?.fields ?? {};
const replyToNameError = errorFields?.reply_to_name ?? '';

View File

@ -9,7 +9,7 @@ export const step: StepType = {
key: 'mailpoet:send-email',
group: 'actions',
title: __('Send email', 'mailpoet'),
description: __('An email will be sent to subscriber.', 'mailpoet'),
description: __('An email will be sent to subscriber', 'mailpoet'),
subtitle: (data) =>
(data.args.name as string) ?? __('Send email', 'mailpoet'),
foreground: '#996800',

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