Autoformat files with prettier
[MAILPOET-4075]
This commit is contained in:
@ -116,7 +116,7 @@ jobs:
|
|||||||
- checkout:
|
- checkout:
|
||||||
path: /home/circleci/mailpoet
|
path: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Compute checksum for prefixer"
|
name: 'Compute checksum for prefixer'
|
||||||
command: find prefixer -type f -not -path 'prefixer/build/*' -not -path 'prefixer/vendor/*' | sort | xargs cat | sha512sum > prefixer-checksum
|
command: find prefixer -type f -not -path 'prefixer/build/*' -not -path 'prefixer/vendor/*' | sort | xargs cat | sha512sum > prefixer-checksum
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: tools-{{ checksum "tools/install.php" }}
|
key: tools-{{ checksum "tools/install.php" }}
|
||||||
@ -133,7 +133,7 @@ jobs:
|
|||||||
- npm-{{ checksum "package-lock.json" }}
|
- npm-{{ checksum "package-lock.json" }}
|
||||||
- npm- # fallback to most recent npm-* if not found by checksum
|
- npm- # fallback to most recent npm-* if not found by checksum
|
||||||
- run:
|
- run:
|
||||||
name: "Set up test environment"
|
name: 'Set up test environment'
|
||||||
command: |
|
command: |
|
||||||
# install plugin dependencies
|
# install plugin dependencies
|
||||||
COMPOSER_DEV_MODE=1 php tools/install.php
|
COMPOSER_DEV_MODE=1 php tools/install.php
|
||||||
@ -197,7 +197,7 @@ jobs:
|
|||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- add_ssh_keys
|
- add_ssh_keys
|
||||||
- run:
|
- run:
|
||||||
name: "Install Premium plugin"
|
name: 'Install Premium plugin'
|
||||||
command: |
|
command: |
|
||||||
# Add GitHub to known_hosts because there is no checkout step in this job
|
# Add GitHub to known_hosts because there is no checkout step in this job
|
||||||
echo "github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" >> ~/.ssh/known_hosts
|
echo "github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" >> ~/.ssh/known_hosts
|
||||||
@ -207,7 +207,7 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: premium-composer-{{ checksum "mailpoet-premium/composer.json" }}-{{ checksum "mailpoet-premium/composer.lock" }}
|
key: premium-composer-{{ checksum "mailpoet-premium/composer.json" }}-{{ checksum "mailpoet-premium/composer.lock" }}
|
||||||
- run:
|
- run:
|
||||||
name: "Set up test environment"
|
name: 'Set up test environment'
|
||||||
command: |
|
command: |
|
||||||
# install Premium dependencies
|
# install Premium dependencies
|
||||||
MAILPOET_FREE_PATH=$(pwd)/mailpoet
|
MAILPOET_FREE_PATH=$(pwd)/mailpoet
|
||||||
@ -241,7 +241,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Static analysis"
|
name: 'Static analysis'
|
||||||
command: ./do qa:phpstan --php-version=<< parameters.php_version >>
|
command: ./do qa:phpstan --php-version=<< parameters.php_version >>
|
||||||
qa_js:
|
qa_js:
|
||||||
executor: wpcli_php_latest
|
executor: wpcli_php_latest
|
||||||
@ -250,7 +250,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "QA Frontend Assets"
|
name: 'QA Frontend Assets'
|
||||||
command: ./do qa:frontend-assets
|
command: ./do qa:frontend-assets
|
||||||
qa_php:
|
qa_php:
|
||||||
executor: wpcli_php_latest
|
executor: wpcli_php_latest
|
||||||
@ -259,7 +259,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "QA PHP"
|
name: 'QA PHP'
|
||||||
command: ./do qa:php
|
command: ./do qa:php
|
||||||
qa_php_oldest:
|
qa_php_oldest:
|
||||||
executor: wpcli_php_oldest
|
executor: wpcli_php_oldest
|
||||||
@ -268,7 +268,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "QA PHP"
|
name: 'QA PHP'
|
||||||
command: ./do qa:php
|
command: ./do qa:php
|
||||||
qa_php_max_wporg:
|
qa_php_max_wporg:
|
||||||
executor: wpcli_php_max_wporg
|
executor: wpcli_php_max_wporg
|
||||||
@ -276,7 +276,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "QA PHP"
|
name: 'QA PHP'
|
||||||
command: ./do qa:php-max-wporg
|
command: ./do qa:php-max-wporg
|
||||||
js_tests:
|
js_tests:
|
||||||
executor: wpcli_php_latest
|
executor: wpcli_php_latest
|
||||||
@ -285,15 +285,15 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Preparing test results folder"
|
name: 'Preparing test results folder'
|
||||||
command: mkdir test-results
|
command: mkdir test-results
|
||||||
- run:
|
- run:
|
||||||
name: "JS Newsletter Editor Tests"
|
name: 'JS Newsletter Editor Tests'
|
||||||
command: |
|
command: |
|
||||||
mkdir test-results/mocha
|
mkdir test-results/mocha
|
||||||
./do t:newsletter-editor test-results/mocha/newsletter_editor_junit.xml
|
./do t:newsletter-editor test-results/mocha/newsletter_editor_junit.xml
|
||||||
- run:
|
- run:
|
||||||
name: "JS Tests"
|
name: 'JS Tests'
|
||||||
command: |
|
command: |
|
||||||
./do t:j test-results/mocha/junit.xml
|
./do t:j test-results/mocha/junit.xml
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
@ -343,10 +343,10 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Set up virtual host"
|
name: 'Set up virtual host'
|
||||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||||
- run:
|
- run:
|
||||||
name: "Pull acceptance test docker images"
|
name: 'Pull acceptance test docker images'
|
||||||
# Pull docker images with 3 retries
|
# 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
|
command: i='0';while ! docker-compose -f tests/docker/docker-compose.yml pull && ((i < 3)); do sleep 3 && i=$[$i+1]; done
|
||||||
- when:
|
- when:
|
||||||
@ -417,16 +417,16 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Set up virtual host"
|
name: 'Set up virtual host'
|
||||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||||
- run:
|
- run:
|
||||||
name: "Prepare example.com for testing"
|
name: 'Prepare example.com for testing'
|
||||||
command: echo 127.0.0.1 example.com | sudo tee -a /etc/hosts
|
command: echo 127.0.0.1 example.com | sudo tee -a /etc/hosts
|
||||||
- run:
|
- run:
|
||||||
name: "Set up test environment"
|
name: 'Set up test environment'
|
||||||
command: source ../.circleci/setup.bash && setup php7
|
command: source ../.circleci/setup.bash && setup php7
|
||||||
- run:
|
- run:
|
||||||
name: "PHP Unit tests"
|
name: 'PHP Unit tests'
|
||||||
command: |
|
command: |
|
||||||
./do t:u --xml
|
./do t:u --xml
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
@ -453,16 +453,16 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Set up virtual host"
|
name: 'Set up virtual host'
|
||||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||||
- run:
|
- run:
|
||||||
name: "Prepare example.com for testing"
|
name: 'Prepare example.com for testing'
|
||||||
command: echo 127.0.0.1 example.com | sudo tee -a /etc/hosts
|
command: echo 127.0.0.1 example.com | sudo tee -a /etc/hosts
|
||||||
- run:
|
- run:
|
||||||
name: "Set up test environment"
|
name: 'Set up test environment'
|
||||||
command: << parameters.setup_command >>
|
command: << parameters.setup_command >>
|
||||||
- run:
|
- run:
|
||||||
name: "PHP Integration tests"
|
name: 'PHP Integration tests'
|
||||||
command: << parameters.run_command >>
|
command: << parameters.run_command >>
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: tests/_output
|
path: tests/_output
|
||||||
@ -479,7 +479,7 @@ jobs:
|
|||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /home/circleci/mailpoet
|
at: /home/circleci/mailpoet
|
||||||
- run:
|
- run:
|
||||||
name: "Set up environment"
|
name: 'Set up environment'
|
||||||
command: |
|
command: |
|
||||||
source ../.circleci/setup.bash && setup php7
|
source ../.circleci/setup.bash && setup php7
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
@ -487,7 +487,7 @@ jobs:
|
|||||||
sed -i 's/^WP_ROOT=.*$/WP_ROOT=\/home\/circleci\/mailpoet\/wordpress/g' .env
|
sed -i 's/^WP_ROOT=.*$/WP_ROOT=\/home\/circleci\/mailpoet\/wordpress/g' .env
|
||||||
echo ${CIRCLE_BUILD_NUM} > release_zip_build_number.txt
|
echo ${CIRCLE_BUILD_NUM} > release_zip_build_number.txt
|
||||||
- run:
|
- run:
|
||||||
name: "Build"
|
name: 'Build'
|
||||||
command: ./build.sh
|
command: ./build.sh
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: /home/circleci/mailpoet/mailpoet/mailpoet.zip
|
path: /home/circleci/mailpoet/mailpoet/mailpoet.zip
|
||||||
@ -580,7 +580,7 @@ workflows:
|
|||||||
nightly:
|
nightly:
|
||||||
triggers:
|
triggers:
|
||||||
- schedule:
|
- schedule:
|
||||||
cron: "0 22 * * 1-5"
|
cron: '0 22 * * 1-5'
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
|
3
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@ -4,7 +4,6 @@ about: Create a report to help us improve
|
|||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
@ -12,6 +11,7 @@ A clear and concise description of what the bug is.
|
|||||||
|
|
||||||
**To reproduce**
|
**To reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to ...
|
1. Go to ...
|
||||||
2. Click on ...
|
2. Click on ...
|
||||||
3. Scroll down to ...
|
3. Scroll down to ...
|
||||||
@ -26,6 +26,7 @@ A clear and concise description of what you expected to happen.
|
|||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Versions (please complete the following information):**
|
**Versions (please complete the following information):**
|
||||||
|
|
||||||
- WordPress version: [e.g: 5.3.2]
|
- WordPress version: [e.g: 5.3.2]
|
||||||
- PHP version: [e.g: 7.4.2]
|
- PHP version: [e.g: 7.4.2]
|
||||||
- MailPoet version: [e.g: 3.46.13]
|
- MailPoet version: [e.g: 3.46.13]
|
||||||
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -4,7 +4,6 @@ about: https://feedback.mailpoet.com/
|
|||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Please use https://feedback.mailpoet.com/ for feature requests.
|
Please use https://feedback.mailpoet.com/ for feature requests.
|
||||||
|
22
.github/SECURITY.md
vendored
22
.github/SECURITY.md
vendored
@ -4,7 +4,7 @@ Full details of the Automattic Security Policy can be found on [automattic.com/s
|
|||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
Generally, *only the latest version of MailPoet has continued support*. If a critical vulnerability is found in the current version of MailPoet, we may opt to backport any patches to previous versions.
|
Generally, _only the latest version of MailPoet has continued support_. If a critical vulnerability is found in the current version of MailPoet, we may opt to backport any patches to previous versions.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
@ -14,9 +14,9 @@ Generally, *only the latest version of MailPoet has continued support*. If a cri
|
|||||||
|
|
||||||
Our most critical targets are:
|
Our most critical targets are:
|
||||||
|
|
||||||
* MailPoet plugin (this repository)
|
- MailPoet plugin (this repository)
|
||||||
* MailPoet Premium
|
- MailPoet Premium
|
||||||
* mailpoet.com -- the primary site, and all of it subdomains, e.g. [account.mailpoet.com](https://account.mailpoet.com/)
|
- mailpoet.com -- the primary site, and all of it subdomains, e.g. [account.mailpoet.com](https://account.mailpoet.com/)
|
||||||
|
|
||||||
For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
|
For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
|
||||||
|
|
||||||
@ -26,12 +26,12 @@ _Please note that the **WordPress software is a separate entity** from Automatti
|
|||||||
|
|
||||||
We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
|
We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
|
||||||
|
|
||||||
* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
|
- Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
|
||||||
* Pen-testing Production:
|
- Pen-testing Production:
|
||||||
* Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
|
- Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
|
||||||
* If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
|
- If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
|
||||||
* **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
|
- **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
|
||||||
* To be eligible for a bounty, please follow all of these guidelines.
|
- To be eligible for a bounty, please follow all of these guidelines.
|
||||||
* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
|
- Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
|
||||||
|
|
||||||
We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
|
We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
# or to provide custom queries or build logic.
|
# or to provide custom queries or build logic.
|
||||||
name: "CodeQL"
|
name: 'CodeQL'
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
## PHP Code
|
## PHP Code
|
||||||
|
|
||||||
- Two spaces indentation.
|
- Two spaces indentation.
|
||||||
- Space between keyword (if, for, switch...) and left bracket
|
- Space between keyword (if, for, switch...) and left bracket
|
||||||
- CamelCase for classes.
|
- CamelCase for classes.
|
||||||
@ -15,20 +16,24 @@
|
|||||||
- Cover your code in tests.
|
- Cover your code in tests.
|
||||||
|
|
||||||
## SCSS Code
|
## SCSS Code
|
||||||
|
|
||||||
- camelCase for file name
|
- camelCase for file name
|
||||||
- Components files are prefixed with underscore, to indicate, that they aren't compiled separately.
|
- Components files are prefixed with underscore, to indicate, that they aren't compiled separately.
|
||||||
|
|
||||||
## JS Code
|
## JS Code
|
||||||
|
|
||||||
- Javascript code should follow the [Airbnb style guide](https://github.com/airbnb/javascript).
|
- Javascript code should follow the [Airbnb style guide](https://github.com/airbnb/javascript).
|
||||||
- Prefer named export before default export in JS and TS files
|
- Prefer named export before default export in JS and TS files
|
||||||
|
|
||||||
## Disabling linting rules
|
## Disabling linting rules
|
||||||
|
|
||||||
- we want to avoid using `eslint-disable`
|
- we want to avoid using `eslint-disable`
|
||||||
- if we have to use it we need to use a comment explaining why do we need it:
|
- if we have to use it we need to use a comment explaining why do we need it:
|
||||||
`/* eslint-disable no-new -- this class has a side-effect in the constructor and it's a library's. */`
|
`/* eslint-disable no-new -- this class has a side-effect in the constructor and it's a library's. */`
|
||||||
- for PHP we do the same with the exception `// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps` which for now doesn’t require an explanation
|
- for PHP we do the same with the exception `// phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps` which for now doesn’t require an explanation
|
||||||
|
|
||||||
## Git flow
|
## Git flow
|
||||||
|
|
||||||
- Do not commit to master.
|
- Do not commit to master.
|
||||||
- Open a short-living feature branch.
|
- Open a short-living feature branch.
|
||||||
- Open a pull request.
|
- Open a pull request.
|
||||||
@ -39,12 +44,13 @@
|
|||||||
- Wait for review from another developer.
|
- Wait for review from another developer.
|
||||||
|
|
||||||
## Issues creation
|
## Issues creation
|
||||||
|
|
||||||
- Issues are managed on Jira.
|
- Issues are managed on Jira.
|
||||||
- Discuss issues on public Slack chats, discuss code in pull requests.
|
- Discuss issues on public Slack chats, discuss code in pull requests.
|
||||||
- Open a small Jira issue only when it has been discussed.
|
- Open a small Jira issue only when it has been discussed.
|
||||||
|
|
||||||
## Migration from IdiORM to Doctrine
|
## Migration from IdiORM to Doctrine
|
||||||
|
|
||||||
MailPoet used to use [IdiORM](https://github.com/j4mie/idiorm) as its object-relational mapper (ORM), but the project was abandoned a while ago, so we started a migration to [Doctrine](https://www.doctrine-project.org/). This is a significant effort that has been going on for quite some time. Although you will still see parts of the code that use IdioORM, we ask that all new code be added using Doctrine instead.
|
MailPoet used to use [IdiORM](https://github.com/j4mie/idiorm) as its object-relational mapper (ORM), but the project was abandoned a while ago, so we started a migration to [Doctrine](https://www.doctrine-project.org/). This is a significant effort that has been going on for quite some time. Although you will still see parts of the code that use IdioORM, we ask that all new code be added using Doctrine instead.
|
||||||
|
|
||||||
All IdioORM models live in [mailpoet/lib/Models](https://github.com/mailpoet/mailpoet/tree/master/mailpoet/lib/Models), should be considered deprecated and shouldn't be used by new code. We are moving everything to Doctrine entities and some auxiliary code when needed. You can find Doctrine entities in [mailpoet/lib/Entities](https://github.com/mailpoet/mailpoet/tree/master/mailpoet/lib/Entities).
|
All IdioORM models live in [mailpoet/lib/Models](https://github.com/mailpoet/mailpoet/tree/master/mailpoet/lib/Models), should be considered deprecated and shouldn't be used by new code. We are moving everything to Doctrine entities and some auxiliary code when needed. You can find Doctrine entities in [mailpoet/lib/Entities](https://github.com/mailpoet/mailpoet/tree/master/mailpoet/lib/Entities).
|
||||||
|
|
||||||
|
29
README.md
29
README.md
@ -1,17 +1,19 @@
|
|||||||
# MailPoet
|
# MailPoet
|
||||||
|
|
||||||
The **MailPoet** plugin monorepo.
|
The **MailPoet** plugin monorepo.
|
||||||
|
|
||||||
To use our Docker-based development environment (recommended), continue with the steps below.
|
To use our Docker-based development environment (recommended), continue with the steps below.
|
||||||
If you'd like to use the plugin code directly, see details in [the plugin's readme](mailpoet/README.md).
|
If you'd like to use the plugin code directly, see details in [the plugin's readme](mailpoet/README.md).
|
||||||
|
|
||||||
## 🔌 Initial setup
|
## 🔌 Initial setup
|
||||||
1) Run `./do setup` to pull everything and install necessary dependencies.
|
|
||||||
2) Add secrets to `.env` files in `mailpoet` and `mailpoet-premium` directories. Go to the Secret Store and look for "MailPoet: plugin .env"
|
|
||||||
3) Run `./do start` to start the stack.
|
|
||||||
4) Go to http://localhost:8888 to see the dashboard of the dev environment.
|
|
||||||
|
|
||||||
|
1. Run `./do setup` to pull everything and install necessary dependencies.
|
||||||
|
2. Add secrets to `.env` files in `mailpoet` and `mailpoet-premium` directories. Go to the Secret Store and look for "MailPoet: plugin .env"
|
||||||
|
3. Run `./do start` to start the stack.
|
||||||
|
4. Go to http://localhost:8888 to see the dashboard of the dev environment.
|
||||||
|
|
||||||
## 🔍 PHPStorm setup for XDebug
|
## 🔍 PHPStorm setup for XDebug
|
||||||
|
|
||||||
In `Languages & Preferences > PHP > Servers` set path mappings:
|
In `Languages & Preferences > PHP > Servers` set path mappings:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -28,6 +30,7 @@ To use XDebug inside the **cron**, you need to pass a URL argument `&XDEBUG_TRIG
|
|||||||
Alternatively, you can add `XDEBUG_TRIGGER: yes` to the `wordpress` service in `docker-compose.yml` and restart it (which will run XDebug also for all other requests).
|
Alternatively, you can add `XDEBUG_TRIGGER: yes` to the `wordpress` service in `docker-compose.yml` and restart it (which will run XDebug also for all other requests).
|
||||||
|
|
||||||
## Xdebug develop mode
|
## Xdebug develop mode
|
||||||
|
|
||||||
[Xdebug develop mode](https://xdebug.org/docs/develop) is disabled by default because it causes performance issues due to conflicts with the DI container.
|
[Xdebug develop mode](https://xdebug.org/docs/develop) is disabled by default because it causes performance issues due to conflicts with the DI container.
|
||||||
|
|
||||||
It can be enabled when needed using the `XDEBUG_MODE` environment variable. For example, it is possible to enable it by adding the following to `docker-compose.override.yml`:
|
It can be enabled when needed using the `XDEBUG_MODE` environment variable. For example, it is possible to enable it by adding the following to `docker-compose.override.yml`:
|
||||||
@ -38,12 +41,15 @@ environment:
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 💾 NFS volume sharing for Mac
|
## 💾 NFS volume sharing for Mac
|
||||||
|
|
||||||
NFS volumes can bring more stability and performance on Docker for Mac. To setup NFS volume sharing run:
|
NFS volumes can bring more stability and performance on Docker for Mac. To setup NFS volume sharing run:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
sudo sh dev/mac-nfs-setup.sh
|
sudo sh dev/mac-nfs-setup.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Then create a Docker Compose override file with NFS settings and restart containers:
|
Then create a Docker Compose override file with NFS settings and restart containers:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cp docker-compose.override.macos-sample.yml docker-compose.override.yml
|
cp docker-compose.override.macos-sample.yml docker-compose.override.yml
|
||||||
|
|
||||||
@ -55,18 +61,22 @@ docker-compose up -d
|
|||||||
outside your `Documents` folder, otherwise you may run into [file permission issues](https://objekt.click/2019/11/docker-the-problem-with-macos-catalina/).
|
outside your `Documents` folder, otherwise you may run into [file permission issues](https://objekt.click/2019/11/docker-the-problem-with-macos-catalina/).
|
||||||
|
|
||||||
# 🐶 Husky
|
# 🐶 Husky
|
||||||
|
|
||||||
We use [Husky](https://github.com/typicode/husky) to run automated checks in pre-commit hooks.
|
We use [Husky](https://github.com/typicode/husky) to run automated checks in pre-commit hooks.
|
||||||
|
|
||||||
In case you're using [NVM](https://github.com/nvm-sh/nvm) for Node version management you may
|
In case you're using [NVM](https://github.com/nvm-sh/nvm) for Node version management you may
|
||||||
need to create or update your `~/.huskyrc` file with:
|
need to create or update your `~/.huskyrc` file with:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# This loads nvm.sh and sets the correct PATH before running the hooks:
|
# This loads nvm.sh and sets the correct PATH before running the hooks:
|
||||||
export NVM_DIR="$HOME/.nvm"
|
export NVM_DIR="$HOME/.nvm"
|
||||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||||
```
|
```
|
||||||
|
|
||||||
Without it, you may experience errors in some Git clients.
|
Without it, you may experience errors in some Git clients.
|
||||||
|
|
||||||
## 🕹 Commands
|
## 🕹 Commands
|
||||||
|
|
||||||
The `./do` script define aliases for most of the commands you will need while working on plugins:
|
The `./do` script define aliases for most of the commands you will need while working on plugins:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@ -88,11 +98,14 @@ Options:
|
|||||||
You can access this help in your command line running `./do` without parameters.
|
You can access this help in your command line running `./do` without parameters.
|
||||||
|
|
||||||
## ✉️ Adding new templates to the plugin
|
## ✉️ Adding new templates to the plugin
|
||||||
|
|
||||||
[Read the article.](https://mailpoet.atlassian.net/wiki/spaces/MAILPOET/pages/629374977/Adding+new+templates+to+the+plugin)
|
[Read the article.](https://mailpoet.atlassian.net/wiki/spaces/MAILPOET/pages/629374977/Adding+new+templates+to+the+plugin)
|
||||||
|
|
||||||
## 🚥 Testing with PHP 7.4 or PHP 8.0
|
## 🚥 Testing with PHP 7.4 or PHP 8.0
|
||||||
|
|
||||||
To switch the environment to PHP 7.4/8.0:
|
To switch the environment to PHP 7.4/8.0:
|
||||||
1) Configure the `wordpress` service in `docker-compose.override.yml` to build from the php74 Dockerfile:
|
|
||||||
|
1. Configure the `wordpress` service in `docker-compose.override.yml` to build from the php74 Dockerfile:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
wordpress:
|
wordpress:
|
||||||
@ -100,11 +113,13 @@ To switch the environment to PHP 7.4/8.0:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: docker/php74/Dockerfile # OR docker/php80/Dockerfile
|
dockerfile: docker/php74/Dockerfile # OR docker/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 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,
|
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`.
|
and start the stack using `./do start`.
|
||||||
|
|
||||||
## ✅ TODO
|
## ✅ TODO
|
||||||
|
|
||||||
- install woo commerce, members and other useful plugins by default
|
- install woo commerce, members and other useful plugins by default
|
||||||
|
@ -8,7 +8,7 @@ thanks for understanding.
|
|||||||
- [Support](https://www.mailpoet.com/support)
|
- [Support](https://www.mailpoet.com/support)
|
||||||
- [Feature Requests](https://feedback.mailpoet.com)
|
- [Feature Requests](https://feedback.mailpoet.com)
|
||||||
|
|
||||||
*DO NOT* use the issue tracker to ask questions;
|
_DO NOT_ use the issue tracker to ask questions;
|
||||||
use the links above for that.
|
use the links above for that.
|
||||||
Questions posed to the issue tracker will be closed.
|
Questions posed to the issue tracker will be closed.
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>MailPoet dev environment</title>
|
<title>MailPoet dev environment</title>
|
||||||
<link rel="icon" href="favicon.png" />
|
<link rel="icon" href="favicon.png" />
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: #ffe0d0;
|
background: #ffe0d0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||||
|
Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||||
|
'Segoe UI Symbol';
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
color: #071c6d;
|
color: #071c6d;
|
||||||
@ -38,9 +40,9 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<a href="/"><img src="logo.svg" /></a>
|
<a href="/"><img src="logo.svg" /></a>
|
||||||
<p>Dev environment</p>
|
<p>Dev environment</p>
|
||||||
<table>
|
<table>
|
||||||
@ -66,11 +68,17 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>💾</td>
|
<td>💾</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="http://localhost:8081?server=db&mysql=wordpress&username=wordpress">Adminer</a>
|
<a
|
||||||
|
href="http://localhost:8081?server=db&mysql=wordpress&username=wordpress"
|
||||||
|
>Adminer</a
|
||||||
|
>
|
||||||
</td>
|
</td>
|
||||||
<td>DB management</td>
|
<td>DB management</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="http://localhost:8081?server=db&mysql=wordpress&username=wordpress">http://localhost:8081</a>
|
<a
|
||||||
|
href="http://localhost:8081?server=db&mysql=wordpress&username=wordpress"
|
||||||
|
>http://localhost:8081</a
|
||||||
|
>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@ -81,5 +89,5 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -4,33 +4,39 @@ This is a place where we put documentation for developers who want to build an e
|
|||||||
If you are a user looking for a user guide please visit our [knowledge base](https://kb.mailpoet.com/).
|
If you are a user looking for a user guide please visit our [knowledge base](https://kb.mailpoet.com/).
|
||||||
|
|
||||||
## MailPoet API
|
## MailPoet API
|
||||||
|
|
||||||
MailPoet API is the officially supported way to integrate with the MailPoet 3 plugin. It focuses on functionality for managing subscribers.
|
MailPoet API is the officially supported way to integrate with the MailPoet 3 plugin. It focuses on functionality for managing subscribers.
|
||||||
Developers integrating MailPoet functionality in their own plugins or projects are strongly discouraged against using other functions and classes within MailPoet codebase! We are continually refactoring as part of our rapid development process, and backward compatibility is not guaranteed.
|
Developers integrating MailPoet functionality in their own plugins or projects are strongly discouraged against using other functions and classes within MailPoet codebase! We are continually refactoring as part of our rapid development process, and backward compatibility is not guaranteed.
|
||||||
|
|
||||||
### Basics
|
### Basics
|
||||||
|
|
||||||
MailPoet API is distributed within MailPoet3 plugin and it is implemented as a PHP class.
|
MailPoet API is distributed within MailPoet3 plugin and it is implemented as a PHP class.
|
||||||
Currently supported version is `v1`.
|
Currently supported version is `v1`.
|
||||||
|
|
||||||
### Instantiation
|
### Instantiation
|
||||||
|
|
||||||
```php
|
```php
|
||||||
if (class_exists(\MailPoet\API\API::class)) {
|
if (class_exists(\MailPoet\API\API::class)) {
|
||||||
$mailpoet_api = \MailPoet\API\API::MP('v1');
|
$mailpoet_api = \MailPoet\API\API::MP('v1');
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Class `\MailPoet\API\API` becomes available once MailPoet plugin is loaded by WordPress.
|
Class `\MailPoet\API\API` becomes available once MailPoet plugin is loaded by WordPress.
|
||||||
|
|
||||||
### Available API Methods
|
### Available API Methods
|
||||||
* [Add List (addList)](api_methods/AddList.md)
|
|
||||||
* [Add Subscriber (addSubscriber)](api_methods/AddSubscriber.md)
|
- [Add List (addList)](api_methods/AddList.md)
|
||||||
* [Add Subscriber Field (addSubscriberField)](api_methods/AddSubscriberField.md)
|
- [Add Subscriber (addSubscriber)](api_methods/AddSubscriber.md)
|
||||||
* [Get Lists (getLists)](api_methods/GetLists.md)
|
- [Add Subscriber Field (addSubscriberField)](api_methods/AddSubscriberField.md)
|
||||||
* [Get Subscriber (getSubscriber)](api_methods/GetSubscriber.md)
|
- [Get Lists (getLists)](api_methods/GetLists.md)
|
||||||
* [Get Subscriber Fields (getSubscriberFields)](api_methods/GetSubscriberFields.md)
|
- [Get Subscriber (getSubscriber)](api_methods/GetSubscriber.md)
|
||||||
* [Is Setup Complete (isSetupComplete)](api_methods/IsSetupComplete.md)
|
- [Get Subscriber Fields (getSubscriberFields)](api_methods/GetSubscriberFields.md)
|
||||||
* [Subscribe to List (subscribeToList)](api_methods/SubscribeToList.md)
|
- [Is Setup Complete (isSetupComplete)](api_methods/IsSetupComplete.md)
|
||||||
* [Subscribe to List (subscribeToLists)](api_methods/SubscribeToLists.md)
|
- [Subscribe to List (subscribeToList)](api_methods/SubscribeToList.md)
|
||||||
* [Unsubscribe from List (unsubscribeFromList)](api_methods/UnsubscribeFromList.md)
|
- [Subscribe to List (subscribeToLists)](api_methods/SubscribeToLists.md)
|
||||||
* [Unsubscribe from Lists (unsubscribeFromLists)](api_methods/UnsubscribeFromLists.md)
|
- [Unsubscribe from List (unsubscribeFromList)](api_methods/UnsubscribeFromList.md)
|
||||||
|
- [Unsubscribe from Lists (unsubscribeFromLists)](api_methods/UnsubscribeFromLists.md)
|
||||||
|
|
||||||
### Usage examples
|
### Usage examples
|
||||||
|
|
||||||
You can check some basic examples [here](UsageExamples.md).
|
You can check some basic examples [here](UsageExamples.md).
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
[back to readme](Readme.md)
|
[back to readme](Readme.md)
|
||||||
|
|
||||||
# Usage Examples
|
# Usage Examples
|
||||||
|
|
||||||
Common usage is a rendering of a subscription form and processing it.
|
Common usage is a rendering of a subscription form and processing it.
|
||||||
|
|
||||||
## Fetching data for a subscription form
|
## Fetching data for a subscription form
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
@ -18,6 +20,7 @@ if (class_exists(\MailPoet\API\API::class)) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Processing a subscription form
|
## Processing a subscription form
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
@ -9,14 +9,15 @@ In MailPoet, subscribers are organized into lists. This method provides function
|
|||||||
It returns the new list. See [Get Lists](GetLists.md) for a list data structure description.
|
It returns the new list. See [Get Lists](GetLists.md) for a list data structure description.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
### `$list` (required)
|
### `$list` (required)
|
||||||
|
|
||||||
An associative array which contains list data.
|
An associative array which contains list data.
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| ---------------------- | ------------ | --------- | -------------------------- |
|
||||||
| name (required) | string | 90 chars | A name of the list. |
|
| name (required) | string | 90 chars | A name of the list. |
|
||||||
| description (optional) | string\|null| 250 chars | A description of the list. |
|
| description (optional) | string\|null | 250 chars | A description of the list. |
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ An exception of base class `\Exception` can be thrown when something unexpected
|
|||||||
Codes description:
|
Codes description:
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
| --- | --- |
|
| ---- | -------------------------------------------- |
|
||||||
| 14 | Missing list name |
|
| 14 | Missing list name |
|
||||||
| 15 | Trying to create a list that already exists |
|
| 15 | Trying to create a list that already exists |
|
||||||
| 16 | The list couldn’t be created in the database |
|
| 16 | The list couldn’t be created in the database |
|
||||||
|
@ -8,9 +8,9 @@ This method allows a subscriber to be created, adds them into lists, and handles
|
|||||||
|
|
||||||
If sign-up confirmation (double opt-in) is enabled in the MailPoet settings a subscriber is created with status `unconfirmed` otherwise the status is set to `subscribed`.
|
If sign-up confirmation (double opt-in) is enabled in the MailPoet settings a subscriber is created with status `unconfirmed` otherwise the status is set to `subscribed`.
|
||||||
|
|
||||||
- *A confirmation email* is an email which is sent to a subscriber so that they can confirm his subscription. It is sent only if sign-up confirmation is enabled in the MailPoet settings.
|
- _A confirmation email_ is an email which is sent to a subscriber so that they can confirm his subscription. It is sent only if sign-up confirmation is enabled in the MailPoet settings.
|
||||||
- *A welcome email* is an automatic email which is sent to a new subscriber. This email is scheduled only if sign-up confirmation is disabled and a welcome email is configured for some of given lists. In case of required sign-up confirmation, it is scheduled later after a subscriber confirms the subscription.
|
- _A welcome email_ is an automatic email which is sent to a new subscriber. This email is scheduled only if sign-up confirmation is disabled and a welcome email is configured for some of given lists. In case of required sign-up confirmation, it is scheduled later after a subscriber confirms the subscription.
|
||||||
- *An admin notification email* is sent to the site admin to inform them about a new subscription. It is sent only if the notification feature is enabled in the MailPoet setting.
|
- _An admin notification email_ is sent to the site admin to inform them about a new subscription. It is sent only if the notification feature is enabled in the MailPoet setting.
|
||||||
|
|
||||||
All these emails can be disabled using `$options`.
|
All these emails can be disabled using `$options`.
|
||||||
|
|
||||||
@ -21,30 +21,33 @@ There might be other `\Exceptions` because of some invalid input data such a inv
|
|||||||
It returns a new subscriber. See [Get Subscriber](GetSubscriber.md) for a subscriber data structure.
|
It returns a new subscriber. See [Get Subscriber](GetSubscriber.md) for a subscriber data structure.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
### `$subscriber` (required)
|
### `$subscriber` (required)
|
||||||
|
|
||||||
An associative array containing subscriber data which contains default properties (email, first_name, last_name) and custom subscriber fields which were defined in MailPoet.
|
An associative array containing subscriber data which contains default properties (email, first_name, last_name) and custom subscriber fields which were defined in MailPoet.
|
||||||
It has to contain an email and all required custom fields. To get defined custom fields see [Get Subscriber Fields](GetSubscriberFields.md)
|
It has to contain an email and all required custom fields. To get defined custom fields see [Get Subscriber Fields](GetSubscriberFields.md)
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| -------------------------- | ------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| email (required) | string | 150 chars | a valid email address |
|
| email (required) | string | 150 chars | a valid email address |
|
||||||
| first_name (optional) | string/null | 255 chars | Fist name of the subscriber. |
|
| first_name (optional) | string/null | 255 chars | Fist name of the subscriber. |
|
||||||
| last_name (optional) | string/null | 255 chars | Last name of the subscriber. |
|
| last_name (optional) | string/null | 255 chars | Last name of the subscriber. |
|
||||||
| cf_* (optional/required) | string/boolean/null | 65K chars | A custom field (see [Get Subscriber Fields](GetSubscriberFields.md)). <br> If a custom field is a checkbox, send truthy or falsy value (`true`/`false, `1`/`0` or `"1"`\`"0"`). |
|
| cf\_\* (optional/required) | string/boolean/null | 65K chars | A custom field (see [Get Subscriber Fields](GetSubscriberFields.md)). <br> If a custom field is a checkbox, send truthy or falsy value (`true`/`false, `1`/`0`or`"1"`\`"0"`). |
|
||||||
|
|
||||||
### `$list_ids` (optional)
|
### `$list_ids` (optional)
|
||||||
|
|
||||||
An array containing list ids into which subscriber will be added.
|
An array containing list ids into which subscriber will be added.
|
||||||
In case that the list is empty a subscriber will be created; but sending a confirmation email, notification email and scheduling welcome email will be skipped.
|
In case that the list is empty a subscriber will be created; but sending a confirmation email, notification email and scheduling welcome email will be skipped.
|
||||||
|
|
||||||
### `$options` (optional)
|
### `$options` (optional)
|
||||||
|
|
||||||
All options are optional. If omitted a default value is used.
|
All options are optional. If omitted a default value is used.
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| Option | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| ---------------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| send_confirmation_email | boolean | true | Can be used to disable a confirmation email. Otherwise, a confirmation email is sent as described above. It is strongly recommended to keep this option set to `true` so that MailPoet settings for sign-up confirmation are respected. Turning it to `false` might lead that subscriber to be added as `unconfirmed`. |
|
| send_confirmation_email | boolean | true | Can be used to disable a confirmation email. Otherwise, a confirmation email is sent as described above. It is strongly recommended to keep this option set to `true` so that MailPoet settings for sign-up confirmation are respected. Turning it to `false` might lead that subscriber to be added as `unconfirmed`. |
|
||||||
| schedule_welcome_email | boolean | true | Can be used to disable a welcome email. Otherwise, a welcome email is scheduled as described above.|
|
| schedule_welcome_email | boolean | true | Can be used to disable a welcome email. Otherwise, a welcome email is scheduled as described above. |
|
||||||
| skip_subscriber_notification | boolean | false | Can be used to disable an admin notification email. Otherwise, an admin notification email is sent as described above.|
|
| skip_subscriber_notification | boolean | false | Can be used to disable an admin notification email. Otherwise, an admin notification email is sent as described above. |
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ An exception of base class `\Exception` can be thrown when something unexpected
|
|||||||
Codes description:
|
Codes description:
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
| --- | --- |
|
| ---- | -------------------------------------------------- |
|
||||||
| 11 | Missing email address |
|
| 11 | Missing email address |
|
||||||
| 12 | Trying to create a subscriber that already exists |
|
| 12 | Trying to create a subscriber that already exists |
|
||||||
| 13 | The subscriber couldn’t be created in the database |
|
| 13 | The subscriber couldn’t be created in the database |
|
||||||
|
@ -12,7 +12,7 @@ See [Subscriber Fields for more details](GetSubscriberFields.md)
|
|||||||
### `$data` (required)
|
### `$data` (required)
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| ----------------- | ------ | -------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| name (required) | string | 90 chars | Human readable name. Intended to be used, as an example, as a label for form input. |
|
| name (required) | string | 90 chars | Human readable name. Intended to be used, as an example, as a label for form input. |
|
||||||
| type (required) | string | - | Type of the field. Possible values are: `text`, `date`, `textarea`, `radio`, `checkbox`, `select` |
|
| type (required) | string | - | Type of the field. Possible values are: `text`, `date`, `textarea`, `radio`, `checkbox`, `select` |
|
||||||
| params (optional) | array | - | Contains various information, see examples below. |
|
| params (optional) | array | - | Contains various information, see examples below. |
|
||||||
@ -23,40 +23,40 @@ Params array differs for each type.
|
|||||||
The common properties for all types:
|
The common properties for all types:
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| -------- | ------ | ------------------------------------------------------------------------------------------- |
|
||||||
| required | string | Indicates if the value must be provided for each subscriber. Possible values are: "1" or "" |
|
| required | string | Indicates if the value must be provided for each subscriber. Possible values are: "1" or "" |
|
||||||
| label | string | Label used for displaying the field to the end user. |
|
| label | string | Label used for displaying the field to the end user. |
|
||||||
|
|
||||||
#### `$params` for text, textarea types
|
#### `$params` for text, textarea types
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| -------- | ------ | ------------------------------------------------------------------------------------------- |
|
||||||
| validate | string | Can be used for validating input values. Possible values are: `number`, `alphanum`, `phone` |
|
| validate | string | Can be used for validating input values. Possible values are: `number`, `alphanum`, `phone` |
|
||||||
|
|
||||||
#### `$params` for checkbox types
|
#### `$params` for checkbox types
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| -------- | ----- | ------------------------------------------------------------ |
|
||||||
| values | array | Same array as for radio type. Must contain exactly 1 element |
|
| values | array | Same array as for radio type. Must contain exactly 1 element |
|
||||||
|
|
||||||
#### `$params` for radio, select types
|
#### `$params` for radio, select types
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| -------- | ----- | --------------------------------------------------------------------------------------------------- |
|
||||||
| values | array | Contains a list of options. Each element must contain a string `value` and can contain `is_checked` |
|
| values | array | Contains a list of options. Each element must contain a string `value` and can contain `is_checked` |
|
||||||
|
|
||||||
#### `$params` for date type
|
#### `$params` for date type
|
||||||
|
|
||||||
| Property | Type | Description |
|
| Property | Type | Description |
|
||||||
| --- | --- | --- |
|
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| date_type | string | Possible values are: Values: `year_month_day`, `year_month`, `month`, `day` |
|
| date_type | string | Possible values are: Values: `year_month_day`, `year_month`, `month`, `day` |
|
||||||
| date_format | string | Values: for year_month_day: `MM/DD/YYYY`, `DD/MM/YYYY`, `YYYY/MM/DD`, for year_month: `YYYY/MM`, `MM/YY`, for year: `YYYY`, for month: `MM` |
|
| date_format | string | Values: for year_month_day: `MM/DD/YYYY`, `DD/MM/YYYY`, `YYYY/MM/DD`, for year_month: `YYYY/MM`, `MM/YY`, for year: `YYYY`, for month: `MM` |
|
||||||
|
|
||||||
## Response
|
## Response
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| ---------- | ------------ | -------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| id | string | 11 chars |Field Id |
|
| id | string | 11 chars | Field Id |
|
||||||
| name | string | 90 chars | Human readable name. Intended to be used, as an example, as a label for form input. |
|
| name | string | 90 chars | Human readable name. Intended to be used, as an example, as a label for form input. |
|
||||||
| type | string | - | Type of the field. Possible values are: `text`, `date`, `textarea`, `radio`, `checkbox`, `select` |
|
| type | string | - | Type of the field. Possible values are: `text`, `date`, `textarea`, `radio`, `checkbox`, `select` |
|
||||||
| params | array | - | Contains various information, see examples below. |
|
| params | array | - | Contains various information, see examples below. |
|
||||||
@ -73,7 +73,7 @@ An exception of base class `\Exception` can be thrown when something unexpected
|
|||||||
Codes description:
|
Codes description:
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
| --- | --- |
|
| ---- | ---------------------------------------------------------------------------------- |
|
||||||
| 1 | The subscriber couldn’t be created in the database |
|
| 1 | The subscriber couldn’t be created in the database |
|
||||||
| 1001 | Missing a mandatory field in the `$data` argument |
|
| 1001 | Missing a mandatory field in the `$data` argument |
|
||||||
| 1002 | A mandatory field in the `$data` argument has wrong type |
|
| 1002 | A mandatory field in the `$data` argument has wrong type |
|
||||||
|
@ -9,7 +9,7 @@ In MailPoet, subscribers are organized into lists. This method returns an array
|
|||||||
### A list data structure
|
### A list data structure
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| ----------- | ------------ | --------- | ----------------------------------------------------------------------------------------------------------- |
|
||||||
| id | string | 11 chars | Id of the list |
|
| id | string | 11 chars | Id of the list |
|
||||||
| name | string | 90 chars | Name of the list |
|
| name | string | 90 chars | Name of the list |
|
||||||
| type | string | - | Type of the list. Currently, there is only one supported value: `default` |
|
| type | string | - | Type of the list. Currently, there is only one supported value: `default` |
|
||||||
@ -19,6 +19,7 @@ In MailPoet, subscribers are organized into lists. This method returns an array
|
|||||||
| deleted_at | string\|null | - | This property is not null only when the list is in the trash. It contains UTC time in 'Y-m-d H:i:s' format. |
|
| deleted_at | string\|null | - | This property is not null only when the list is in the trash. It contains UTC time in 'Y-m-d H:i:s' format. |
|
||||||
|
|
||||||
### Response Example
|
### Response Example
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
[
|
[
|
||||||
|
@ -7,15 +7,17 @@
|
|||||||
This method throws an `\Exception` in the event a subscriber with a given email address doesn’t exist.
|
This method throws an `\Exception` in the event a subscriber with a given email address doesn’t exist.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
| Argument | Type | Description |
|
| Argument | Type | Description |
|
||||||
| --- | --- | --- |
|
| ----------------- | ------ | --------------------- |
|
||||||
| $subscriber_email | string | a valid email address |
|
| $subscriber_email | string | a valid email address |
|
||||||
|
|
||||||
## A subscriber data structure
|
## A subscriber data structure
|
||||||
|
|
||||||
### Subscriber
|
### Subscriber
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| ------------------------ | ------------ | --------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| id | string | 11 chars | Id of the subscriber |
|
| id | string | 11 chars | Id of the subscriber |
|
||||||
| wp_user_id | string\|null | 20 chars | Id of a WordPress user associated with the subscriber |
|
| wp_user_id | string\|null | 20 chars | Id of a WordPress user associated with the subscriber |
|
||||||
| is_woocommerce_user | string | - | A flag telling whether the user is also a WooCommerce customer. Possible values are: `1`, `0` |
|
| is_woocommerce_user | string | - | A flag telling whether the user is also a WooCommerce customer. Possible values are: `1`, `0` |
|
||||||
@ -30,14 +32,15 @@ This method throws an `\Exception` in the event a subscriber with a given email
|
|||||||
| updated_at | string | - | UTC time of last update in 'Y-m-d H:i:s' format |
|
| updated_at | string | - | UTC time of last update in 'Y-m-d H:i:s' format |
|
||||||
| deleted_at | string\|null | - | This property in not null in case that list is in trash and contains UTC time in 'Y-m-d H:i:s' format. |
|
| deleted_at | string\|null | - | This property in not null in case that list is in trash and contains UTC time in 'Y-m-d H:i:s' format. |
|
||||||
| unconfirmed_data | string\|null | 65K chars | May contain serialized subscriber data in case when there are pending changes waiting for a confirmation from a subscriber |
|
| unconfirmed_data | string\|null | 65K chars | May contain serialized subscriber data in case when there are pending changes waiting for a confirmation from a subscriber |
|
||||||
| source | string\|null | - | Possible values: `form`,`imported`,`administrator`,`api`,`wordpress_user`,`woocommerce_user`,`woocommerce_checkout`,`unknown`)
|
| source | string\|null | - | Possible values: `form`,`imported`,`administrator`,`api`,`wordpress_user`,`woocommerce_user`,`woocommerce_checkout`,`unknown`) |
|
||||||
| count_confirmations | string | 11 chars | Counter for confirmation emails |
|
| count_confirmations | string | 11 chars | Counter for confirmation emails |
|
||||||
| subscriptions | array | - | List of subcriber subscriptions |
|
| subscriptions | array | - | List of subcriber subscriptions |
|
||||||
| cf_{custom_field['id']}| string | 65K chars | A custom subscriber field value (see [Get Subscriber Fields](GetSubscriberFields.md)|
|
| cf\_{custom_field['id']} | string | 65K chars | A custom subscriber field value (see [Get Subscriber Fields](GetSubscriberFields.md) |
|
||||||
|
|
||||||
### Subscriber's subscription
|
### Subscriber's subscription
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| ------------- | ------ | -------- | ------------------------------------------------------------------------------------ |
|
||||||
| id | string | 11 chars | Id of relation |
|
| id | string | 11 chars | Id of relation |
|
||||||
| subscriber_id | string | 11 chars | Id of subscriber |
|
| subscriber_id | string | 11 chars | Id of subscriber |
|
||||||
| segment_id | string | 11 chars | Id of a list |
|
| segment_id | string | 11 chars | Id of a list |
|
||||||
@ -46,6 +49,7 @@ This method throws an `\Exception` in the event a subscriber with a given email
|
|||||||
| updated_at | string | - | UTC time of last update in 'Y-m-d H:i:s' format |
|
| updated_at | string | - | UTC time of last update in 'Y-m-d H:i:s' format |
|
||||||
|
|
||||||
### Response Example
|
### Response Example
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
[
|
[
|
||||||
@ -98,5 +102,5 @@ An exception of base class `\Exception` can be thrown when something unexpected
|
|||||||
Codes description:
|
Codes description:
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
| --- | --- |
|
| ---- | -------------------------------------------- |
|
||||||
| 4 | Asking for a subscriber that does not exist. |
|
| 4 | Asking for a subscriber that does not exist. |
|
||||||
|
@ -12,13 +12,14 @@ See also [addSubscriberField function.](AddSubscriberField.md)
|
|||||||
## Subscriber Field
|
## Subscriber Field
|
||||||
|
|
||||||
| Property | Type | Limits | Description |
|
| Property | Type | Limits | Description |
|
||||||
| --- | --- | --- | --- |
|
| -------- | ------ | -------- | ------------------------------------------------------------------------------------------------- |
|
||||||
| id | string | 11 chars |Field Id |
|
| id | string | 11 chars | Field Id |
|
||||||
| name | string | 90 chars | Human readable name. Intended to be used, as an example, as a label for form input. |
|
| name | string | 90 chars | Human readable name. Intended to be used, as an example, as a label for form input. |
|
||||||
| type | string | - | Type of the field. Possible values are: `text`, `date`, `textarea`, `radio`, `checkbox`, `select` |
|
| type | string | - | Type of the field. Possible values are: `text`, `date`, `textarea`, `radio`, `checkbox`, `select` |
|
||||||
| params | array | - | Contains various information, see examples below. |
|
| params | array | - | Contains various information, see examples below. |
|
||||||
|
|
||||||
## Response Example
|
## Response Example
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
[
|
[
|
||||||
|
@ -6,29 +6,33 @@
|
|||||||
|
|
||||||
This method allows adding an existing subscriber into lists and handles confirmation email and admin notification email sending.
|
This method allows adding an existing subscriber into lists and handles confirmation email and admin notification email sending.
|
||||||
|
|
||||||
- *A confirmation email* is an email which is sent to a subscriber so they can confirm their subscription. It is sent only if sign-up confirmation is enabled in MailPoet settings and subscriber has not received any confirmation email yet.
|
- _A confirmation email_ is an email which is sent to a subscriber so they can confirm their subscription. It is sent only if sign-up confirmation is enabled in MailPoet settings and subscriber has not received any confirmation email yet.
|
||||||
- *A welcome email* is an automatic email which is sent to a new subscriber. This email is scheduled only if sign-up confirmation is disabled and some welcome email is configured for some of given lists.
|
- _A welcome email_ is an automatic email which is sent to a new subscriber. This email is scheduled only if sign-up confirmation is disabled and some welcome email is configured for some of given lists.
|
||||||
- *An admin notification email* is sent to the site admin to inform them about a new subscription. It is sent only if the notification feature is enabled in the MailPoet setting.
|
- _An admin notification email_ is sent to the site admin to inform them about a new subscription. It is sent only if the notification feature is enabled in the MailPoet setting.
|
||||||
|
|
||||||
All these emails can be disabled using `$options`.
|
All these emails can be disabled using `$options`.
|
||||||
|
|
||||||
It returns a subscriber. See [Get Subscriber](GetSubscriber.md) for a subscriber data structure.
|
It returns a subscriber. See [Get Subscriber](GetSubscriber.md) for a subscriber data structure.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
### string `$subscriber_id` (required)
|
### string `$subscriber_id` (required)
|
||||||
|
|
||||||
An id or an email address. An `\Exception` is thrown when the value doesn't match any subscriber.
|
An id or an email address. An `\Exception` is thrown when the value doesn't match any subscriber.
|
||||||
|
|
||||||
### array `$list_ids` (required)
|
### array `$list_ids` (required)
|
||||||
|
|
||||||
An array of list ids. An `\Exception` is thrown if any of list ids are invalid. In such a case the subscriber isn't added to any list.
|
An array of list ids. An `\Exception` is thrown if any of list ids are invalid. In such a case the subscriber isn't added to any list.
|
||||||
|
|
||||||
### array `$options` (optional)
|
### array `$options` (optional)
|
||||||
|
|
||||||
All options are optional. If omitted, a default value is used.
|
All options are optional. If omitted, a default value is used.
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| Option | Type | Default | Description |
|
||||||
| --- | --- | --- | --- |
|
| ---------------------------- | ------- | ------- | ---------------------------------------------------------------------------------------------------------------------- |
|
||||||
| send_confirmation_email | boolean | true | Can be used to disable confirmation email. Otherwise, a confirmation email is sent as described above.|
|
| send_confirmation_email | boolean | true | Can be used to disable confirmation email. Otherwise, a confirmation email is sent as described above. |
|
||||||
| schedule_welcome_email | boolean | true | Can be used to disable welcome email. Otherwise, a welcome email is scheduled as described above.|
|
| schedule_welcome_email | boolean | true | Can be used to disable welcome email. Otherwise, a welcome email is scheduled as described above. |
|
||||||
| skip_subscriber_notification | boolean | false | Can be used to disable an admin notification email. Otherwise, an admin notification email is sent as described above.|
|
| skip_subscriber_notification | boolean | false | Can be used to disable an admin notification email. Otherwise, an admin notification email is sent as described above. |
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
@ -40,7 +44,7 @@ An exception of base class `\Exception` can be thrown when something unexpected
|
|||||||
Codes description:
|
Codes description:
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
| --- | --- |
|
| ---- | ------------------------------------------------------- |
|
||||||
| 3 | No lists provided |
|
| 3 | No lists provided |
|
||||||
| 4 | Invalid subscriber that does not exist |
|
| 4 | Invalid subscriber that does not exist |
|
||||||
| 5 | Invalid list that does not exist |
|
| 5 | Invalid list that does not exist |
|
||||||
|
@ -9,10 +9,13 @@ This method removes a subscriber from given lists.
|
|||||||
It returns a subscriber. See [Get Subscriber](GetSubscriber.md) for a subscriber data structure.
|
It returns a subscriber. See [Get Subscriber](GetSubscriber.md) for a subscriber data structure.
|
||||||
|
|
||||||
## Arguments
|
## Arguments
|
||||||
|
|
||||||
### string `$subscriber_id` (required)
|
### string `$subscriber_id` (required)
|
||||||
|
|
||||||
An id or email of an existing subscriber. An `\Exception` is thrown when an id or email doesn't match any subscriber.
|
An id or email of an existing subscriber. An `\Exception` is thrown when an id or email doesn't match any subscriber.
|
||||||
|
|
||||||
### array `$list_ids` (required)
|
### array `$list_ids` (required)
|
||||||
|
|
||||||
An array of list ids. An `\Exception` is thrown if any of list ids are invalid. In such a case the subscriber remains subscribed to all lists.
|
An array of list ids. An `\Exception` is thrown if any of list ids are invalid. In such a case the subscriber remains subscribed to all lists.
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
@ -25,7 +28,7 @@ An exception of base class `\Exception` can be thrown when something unexpected
|
|||||||
Codes description:
|
Codes description:
|
||||||
|
|
||||||
| Code | Description |
|
| Code | Description |
|
||||||
| --- | --- |
|
| ---- | --------------------------------------------------- |
|
||||||
| 3 | No lists provided |
|
| 3 | No lists provided |
|
||||||
| 4 | Invalid subscriber that does not exist |
|
| 4 | Invalid subscriber that does not exist |
|
||||||
| 5 | Invalid list that does not exist |
|
| 5 | Invalid list that does not exist |
|
||||||
|
@ -22,16 +22,16 @@ volumes:
|
|||||||
driver_opts:
|
driver_opts:
|
||||||
type: nfs
|
type: nfs
|
||||||
o: addr=host.docker.internal,nolock
|
o: addr=host.docker.internal,nolock
|
||||||
device: ":/System/Volumes/Data${PWD}/wordpress"
|
device: ':/System/Volumes/Data${PWD}/wordpress'
|
||||||
nfs-mailpoet:
|
nfs-mailpoet:
|
||||||
driver: local
|
driver: local
|
||||||
driver_opts:
|
driver_opts:
|
||||||
type: nfs
|
type: nfs
|
||||||
o: addr=host.docker.internal,nolock
|
o: addr=host.docker.internal,nolock
|
||||||
device: ":/System/Volumes/Data${PWD}/mailpoet"
|
device: ':/System/Volumes/Data${PWD}/mailpoet'
|
||||||
nfs-mailpoet-premium:
|
nfs-mailpoet-premium:
|
||||||
driver: local
|
driver: local
|
||||||
driver_opts:
|
driver_opts:
|
||||||
type: nfs
|
type: nfs
|
||||||
o: addr=host.docker.internal,nolock
|
o: addr=host.docker.internal,nolock
|
||||||
device: ":/System/Volumes/Data${PWD}/mailpoet-premium"
|
device: ':/System/Volumes/Data${PWD}/mailpoet-premium'
|
||||||
|
@ -31,8 +31,8 @@ services:
|
|||||||
UID: ${UID:-1000}
|
UID: ${UID:-1000}
|
||||||
GID: ${GID:-1000}
|
GID: ${GID:-1000}
|
||||||
ports:
|
ports:
|
||||||
- "8002:80"
|
- '8002:80'
|
||||||
- "8083:8083" # Storybook port number, see package.json
|
- '8083:8083' # Storybook port number, see package.json
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
- smtp
|
- smtp
|
||||||
@ -43,17 +43,17 @@ services:
|
|||||||
WORDPRESS_DB_HOST: db:3306
|
WORDPRESS_DB_HOST: db:3306
|
||||||
WORDPRESS_DB_USER: wordpress
|
WORDPRESS_DB_USER: wordpress
|
||||||
WORDPRESS_DB_PASSWORD: wordpress
|
WORDPRESS_DB_PASSWORD: wordpress
|
||||||
PHP_IDE_CONFIG: "serverName=Mailpoet"
|
PHP_IDE_CONFIG: 'serverName=Mailpoet'
|
||||||
COMPOSER_HOME: "/tmp/.composer"
|
COMPOSER_HOME: '/tmp/.composer'
|
||||||
NPM_CONFIG_CACHE: "/tmp/.npm"
|
NPM_CONFIG_CACHE: '/tmp/.npm'
|
||||||
MAILPOET_DEV_SITE: 1
|
MAILPOET_DEV_SITE: 1
|
||||||
volumes:
|
volumes:
|
||||||
- "./wordpress:/var/www/html"
|
- './wordpress:/var/www/html'
|
||||||
- "./eslint-config:/var/www/html/wp-content/plugins/eslint-config"
|
- './eslint-config:/var/www/html/wp-content/plugins/eslint-config'
|
||||||
- "./tsconfig.base.json:/var/www/html/wp-content/plugins/tsconfig.base.json:ro"
|
- './tsconfig.base.json:/var/www/html/wp-content/plugins/tsconfig.base.json:ro'
|
||||||
- "./mailpoet:/var/www/html/wp-content/plugins/mailpoet"
|
- './mailpoet:/var/www/html/wp-content/plugins/mailpoet'
|
||||||
- "./mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium"
|
- './mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium'
|
||||||
- "./templates:/var/www/templates"
|
- './templates:/var/www/templates'
|
||||||
|
|
||||||
test_wordpress:
|
test_wordpress:
|
||||||
container_name: mp-test-wp
|
container_name: mp-test-wp
|
||||||
@ -64,7 +64,7 @@ services:
|
|||||||
UID: ${UID:-1000}
|
UID: ${UID:-1000}
|
||||||
GID: ${GID:-1000}
|
GID: ${GID:-1000}
|
||||||
ports:
|
ports:
|
||||||
- "8003:80"
|
- '8003:80'
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
- smtp
|
- smtp
|
||||||
@ -74,11 +74,11 @@ services:
|
|||||||
WORDPRESS_DB_HOST: db:3306
|
WORDPRESS_DB_HOST: db:3306
|
||||||
WORDPRESS_DB_USER: wordpress
|
WORDPRESS_DB_USER: wordpress
|
||||||
WORDPRESS_DB_PASSWORD: wordpress
|
WORDPRESS_DB_PASSWORD: wordpress
|
||||||
PHP_IDE_CONFIG: "serverName=Mailpoet"
|
PHP_IDE_CONFIG: 'serverName=Mailpoet'
|
||||||
volumes:
|
volumes:
|
||||||
- "./eslint-config:/var/www/html/wp-content/plugins/eslint-config"
|
- './eslint-config:/var/www/html/wp-content/plugins/eslint-config'
|
||||||
- "./mailpoet:/var/www/html/wp-content/plugins/mailpoet"
|
- './mailpoet:/var/www/html/wp-content/plugins/mailpoet'
|
||||||
- "./mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium"
|
- './mailpoet-premium:/var/www/html/wp-content/plugins/mailpoet-premium'
|
||||||
|
|
||||||
smtp:
|
smtp:
|
||||||
container_name: mp-mailhog
|
container_name: mp-mailhog
|
||||||
@ -88,9 +88,9 @@ services:
|
|||||||
MH_STORAGE: maildir
|
MH_STORAGE: maildir
|
||||||
MH_MAILDIR_PATH: /output
|
MH_MAILDIR_PATH: /output
|
||||||
volumes:
|
volumes:
|
||||||
- "./dev/data/mailhog:/output"
|
- './dev/data/mailhog:/output'
|
||||||
ports:
|
ports:
|
||||||
- "8082:8025"
|
- '8082:8025'
|
||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
container_name: mp-adminer
|
container_name: mp-adminer
|
||||||
@ -98,9 +98,9 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
ports:
|
ports:
|
||||||
- "8081:8080"
|
- '8081:8080'
|
||||||
volumes:
|
volumes:
|
||||||
- "./dev/php.ini:/usr/local/etc/php/conf.d/custom.ini"
|
- './dev/php.ini:/usr/local/etc/php/conf.d/custom.ini'
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
my-datavolume:
|
my-datavolume:
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["airbnb", "plugin:react/jsx-runtime", "prettier"],
|
||||||
"airbnb",
|
|
||||||
"plugin:react/jsx-runtime",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true
|
"browser": true
|
||||||
},
|
},
|
||||||
@ -14,9 +10,7 @@
|
|||||||
"jsx": true
|
"jsx": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": ["react-hooks"],
|
||||||
"react-hooks"
|
|
||||||
],
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": "webpack"
|
"import/resolver": "webpack"
|
||||||
},
|
},
|
||||||
@ -27,9 +21,12 @@
|
|||||||
"react-hooks/exhaustive-deps": "warn",
|
"react-hooks/exhaustive-deps": "warn",
|
||||||
// Exceptions
|
// Exceptions
|
||||||
"import/extensions": 0, // we wouldn't be able to import jQuery without this line
|
"import/extensions": 0, // we wouldn't be able to import jQuery without this line
|
||||||
"jsx-a11y/anchor-is-valid": [ "error", {
|
"jsx-a11y/anchor-is-valid": [
|
||||||
"components": [ "Link" ],
|
"error",
|
||||||
"specialLink": [ "to" ]
|
{
|
||||||
}]
|
"components": ["Link"],
|
||||||
|
"specialLink": ["to"]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,7 @@
|
|||||||
"jsx": true
|
"jsx": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": ["react-hooks", "no-only-tests", "@typescript-eslint"],
|
||||||
"react-hooks",
|
|
||||||
"no-only-tests",
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": "webpack"
|
"import/resolver": "webpack"
|
||||||
},
|
},
|
||||||
@ -49,19 +45,28 @@
|
|||||||
"import/prefer-default-export": 0, // we want to stop using default exports and start using named exports
|
"import/prefer-default-export": 0, // we want to stop using default exports and start using named exports
|
||||||
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
|
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
|
||||||
"prefer-destructuring": 0, // that would be too many changes to fix this one
|
"prefer-destructuring": 0, // that would be too many changes to fix this one
|
||||||
"jsx-a11y/label-has-for": [2, {
|
"jsx-a11y/label-has-for": [
|
||||||
"required": {"some": ["nesting", "id"]} // some of our labels are hidden and we cannot nest those
|
2,
|
||||||
}],
|
{
|
||||||
|
"required": { "some": ["nesting", "id"] } // some of our labels are hidden and we cannot nest those
|
||||||
|
}
|
||||||
|
],
|
||||||
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
|
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
|
||||||
"jsx-a11y/label-has-associated-control": [ 2, {
|
"jsx-a11y/label-has-associated-control": [
|
||||||
|
2,
|
||||||
|
{
|
||||||
"either": "either" // control has to be either nested or associated via htmlFor
|
"either": "either" // control has to be either nested or associated via htmlFor
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["**/_stories/*.tsx"],
|
"files": ["**/_stories/*.tsx"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }]
|
"import/no-extraneous-dependencies": [
|
||||||
|
"error",
|
||||||
|
{ "devDependencies": true }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["airbnb/legacy", "prettier"],
|
||||||
"airbnb/legacy",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"env": {
|
"env": {
|
||||||
"amd": true,
|
"amd": true,
|
||||||
"browser": true
|
"browser": true
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": ["airbnb", "plugin:react/jsx-runtime", "prettier"],
|
||||||
"airbnb",
|
|
||||||
"plugin:react/jsx-runtime",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"env": {
|
"env": {
|
||||||
"amd": true,
|
"amd": true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
@ -16,10 +12,7 @@
|
|||||||
"jsx": true
|
"jsx": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": ["react-hooks", "no-only-tests"],
|
||||||
"react-hooks",
|
|
||||||
"no-only-tests"
|
|
||||||
],
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": "webpack"
|
"import/resolver": "webpack"
|
||||||
},
|
},
|
||||||
@ -36,13 +29,19 @@
|
|||||||
"import/prefer-default-export": 0, // we want to stop using default exports and start using named exports
|
"import/prefer-default-export": 0, // we want to stop using default exports and start using named exports
|
||||||
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
|
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
|
||||||
"prefer-destructuring": 0, // that would be too many changes to fix this one
|
"prefer-destructuring": 0, // that would be too many changes to fix this one
|
||||||
"jsx-a11y/label-has-for": [2, {
|
"jsx-a11y/label-has-for": [
|
||||||
"required": {"some": ["nesting", "id"]} // some of our labels are hidden and we cannot nest those
|
2,
|
||||||
}],
|
{
|
||||||
|
"required": { "some": ["nesting", "id"] } // some of our labels are hidden and we cannot nest those
|
||||||
|
}
|
||||||
|
],
|
||||||
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
|
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
|
||||||
"jsx-a11y/label-has-associated-control": [ 2, {
|
"jsx-a11y/label-has-associated-control": [
|
||||||
|
2,
|
||||||
|
{
|
||||||
"either": "either" // control has to be either nested or associated via htmlFor
|
"either": "either" // control has to be either nested or associated via htmlFor
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
@ -53,4 +52,3 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,11 +22,7 @@
|
|||||||
"jsx": true
|
"jsx": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": ["react-hooks", "no-only-tests", "@typescript-eslint"],
|
||||||
"react-hooks",
|
|
||||||
"no-only-tests",
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": "webpack",
|
"import/resolver": "webpack",
|
||||||
"import/core-modules": [
|
"import/core-modules": [
|
||||||
@ -45,10 +41,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"react/no-unstable-nested-components": [
|
"react/no-unstable-nested-components": ["error", { "allowAsProps": true }],
|
||||||
"error",
|
|
||||||
{ "allowAsProps": true }
|
|
||||||
],
|
|
||||||
// PropTypes
|
// PropTypes
|
||||||
"react/prop-types": 0,
|
"react/prop-types": 0,
|
||||||
"react/jsx-props-no-spreading": 0,
|
"react/jsx-props-no-spreading": 0,
|
||||||
@ -59,7 +52,7 @@
|
|||||||
// Exceptions
|
// Exceptions
|
||||||
"@typescript-eslint/no-explicit-any": "error", // make it an error instead of warning - we treat them the same, this is more visible
|
"@typescript-eslint/no-explicit-any": "error", // make it an error instead of warning - we treat them the same, this is more visible
|
||||||
"no-void": 0, // can conflict with @typescript-eslint/no-floating-promises
|
"no-void": 0, // can conflict with @typescript-eslint/no-floating-promises
|
||||||
"react/jsx-no-useless-fragment" : [
|
"react/jsx-no-useless-fragment": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"allowExpressions": true
|
"allowExpressions": true
|
||||||
@ -76,13 +69,19 @@
|
|||||||
"import/prefer-default-export": 0, // we want to stop using default exports and start using named exports
|
"import/prefer-default-export": 0, // we want to stop using default exports and start using named exports
|
||||||
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
|
"react/destructuring-assignment": 0, // that would be too many changes to fix this one
|
||||||
"prefer-destructuring": 0, // that would be too many changes to fix this one
|
"prefer-destructuring": 0, // that would be too many changes to fix this one
|
||||||
"jsx-a11y/label-has-for": [2, {
|
"jsx-a11y/label-has-for": [
|
||||||
"required": {"some": ["nesting", "id"]} // some of our labels are hidden and we cannot nest those
|
2,
|
||||||
}],
|
{
|
||||||
|
"required": { "some": ["nesting", "id"] } // some of our labels are hidden and we cannot nest those
|
||||||
|
}
|
||||||
|
],
|
||||||
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
|
"jsx-a11y/anchor-is-valid": 0, // cannot fix this one, it would break wprdpress themes
|
||||||
"jsx-a11y/label-has-associated-control": [ 2, {
|
"jsx-a11y/label-has-associated-control": [
|
||||||
|
2,
|
||||||
|
{
|
||||||
"either": "either" // control has to be either nested or associated via htmlFor
|
"either": "either" // control has to be either nested or associated via htmlFor
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
@ -96,7 +95,10 @@
|
|||||||
{
|
{
|
||||||
"files": ["**/_stories/*.tsx"],
|
"files": ["**/_stories/*.tsx"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"import/no-extraneous-dependencies": ["error", { "devDependencies": true }]
|
"import/no-extraneous-dependencies": [
|
||||||
|
"error",
|
||||||
|
{ "devDependencies": true }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const modulesDir = path.join( __dirname, '../node_modules' );
|
const modulesDir = path.join(__dirname, '../node_modules');
|
||||||
console.log('NODE', modulesDir);
|
console.log('NODE', modulesDir);
|
||||||
// Workaround for Emotion 11
|
// Workaround for Emotion 11
|
||||||
// https://github.com/storybookjs/storybook/pull/13300#issuecomment-783268111
|
// https://github.com/storybookjs/storybook/pull/13300#issuecomment-783268111
|
||||||
|
@ -6,4 +6,9 @@ import '../assets/dist/css/mailpoet-plugin.css';
|
|||||||
import '../assets/dist/css/mailpoet-form-editor.css';
|
import '../assets/dist/css/mailpoet-form-editor.css';
|
||||||
|
|
||||||
addDecorator(withPerformance);
|
addDecorator(withPerformance);
|
||||||
addDecorator(story => <div className="wp-core-ui" id="wpbody"><div id="mailpoet-modal"></div>{story()}</div>);
|
addDecorator((story) => (
|
||||||
|
<div className="wp-core-ui" id="wpbody">
|
||||||
|
<div id="mailpoet-modal"></div>
|
||||||
|
{story()}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
@ -1,110 +1,106 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
'plugins': ['stylelint-order', 'stylelint-scss'],
|
||||||
"stylelint-order",
|
'customSyntax': 'postcss-scss',
|
||||||
"stylelint-scss"
|
'rules':
|
||||||
|
{
|
||||||
|
'at-rule-empty-line-before':
|
||||||
|
['always', { except: ['first-nested', 'blockless-after-blockless'] }],
|
||||||
|
'at-rule-name-case': 'lower',
|
||||||
|
'at-rule-semicolon-newline-after': 'always',
|
||||||
|
'block-closing-brace-empty-line-before': 'never',
|
||||||
|
'block-closing-brace-newline-after': 'always',
|
||||||
|
'block-closing-brace-newline-before': 'always-multi-line',
|
||||||
|
'block-closing-brace-space-before': 'always-single-line',
|
||||||
|
'block-no-empty': true,
|
||||||
|
'block-opening-brace-newline-after': 'always-multi-line',
|
||||||
|
'block-opening-brace-space-after': 'always-single-line',
|
||||||
|
'block-opening-brace-space-before': 'always',
|
||||||
|
'color-hex-case': 'lower',
|
||||||
|
'color-hex-length': 'short',
|
||||||
|
'color-no-invalid-hex': true,
|
||||||
|
'comment-no-empty': true,
|
||||||
|
'comment-whitespace-inside': 'always',
|
||||||
|
'declaration-bang-space-after': 'never',
|
||||||
|
'declaration-bang-space-before': 'always',
|
||||||
|
'declaration-block-no-duplicate-properties':
|
||||||
|
[true, { ignore: ['consecutive-duplicates-with-different-values'] }],
|
||||||
|
'declaration-block-no-redundant-longhand-properties':
|
||||||
|
[true, { ignoreShorthands: [/flex/, /grid/] }],
|
||||||
|
'declaration-block-semicolon-newline-after': 'always-multi-line',
|
||||||
|
'declaration-block-semicolon-space-after': 'always-single-line',
|
||||||
|
'declaration-block-semicolon-space-before': 'never',
|
||||||
|
'declaration-block-single-line-max-declarations': 1,
|
||||||
|
'declaration-colon-newline-after': 'always-multi-line',
|
||||||
|
'declaration-colon-space-after': 'always-single-line',
|
||||||
|
'declaration-colon-space-before': 'never',
|
||||||
|
'declaration-empty-line-before': 'never',
|
||||||
|
'font-family-no-duplicate-names': true,
|
||||||
|
'function-comma-space-after': 'always-single-line',
|
||||||
|
'function-comma-space-before': 'never',
|
||||||
|
'function-max-empty-lines': 0,
|
||||||
|
'function-name-case': 'lower',
|
||||||
|
'function-parentheses-newline-inside': 'always-multi-line',
|
||||||
|
'function-parentheses-space-inside': 'never-single-line',
|
||||||
|
'function-url-quotes': 'always',
|
||||||
|
'function-whitespace-after': 'always',
|
||||||
|
'indentation': 2,
|
||||||
|
'keyframe-declaration-no-important': true,
|
||||||
|
'length-zero-no-unit': true,
|
||||||
|
'max-empty-lines': 1,
|
||||||
|
'media-feature-colon-space-after': 'always',
|
||||||
|
'media-feature-colon-space-before': 'never',
|
||||||
|
'media-feature-name-case': 'lower',
|
||||||
|
'media-feature-name-no-unknown': true,
|
||||||
|
'media-feature-parentheses-space-inside': 'never',
|
||||||
|
'media-feature-range-operator-space-after': 'always',
|
||||||
|
'media-query-list-comma-newline-after': 'always-multi-line',
|
||||||
|
'media-query-list-comma-space-after': 'always-single-line',
|
||||||
|
'media-query-list-comma-space-before': 'never',
|
||||||
|
'no-duplicate-selectors': true,
|
||||||
|
'no-eol-whitespace': true,
|
||||||
|
'no-extra-semicolons': true,
|
||||||
|
'no-missing-end-of-source-newline': true,
|
||||||
|
'number-leading-zero': 'never',
|
||||||
|
'number-no-trailing-zeros': true,
|
||||||
|
'order/properties-alphabetical-order': true,
|
||||||
|
'property-case': 'lower',
|
||||||
|
'property-no-unknown': true,
|
||||||
|
'rule-empty-line-before':
|
||||||
|
[
|
||||||
|
'always-multi-line',
|
||||||
|
{ except: ['first-nested'], ignore: ['after-comment'] },
|
||||||
],
|
],
|
||||||
"customSyntax": "postcss-scss",
|
'scss/at-rule-no-unknown': true,
|
||||||
"rules": {
|
'scss/dollar-variable-colon-space-after': 'always',
|
||||||
"at-rule-empty-line-before": ["always", {
|
'scss/dollar-variable-colon-space-before': 'never',
|
||||||
except: ["first-nested", "blockless-after-blockless"],
|
'scss/operator-no-newline-after': true,
|
||||||
}],
|
'scss/operator-no-newline-before': true,
|
||||||
"at-rule-name-case": "lower",
|
'scss/operator-no-unspaced': true,
|
||||||
"at-rule-semicolon-newline-after": "always",
|
'scss/selector-no-redundant-nesting-selector': true,
|
||||||
"block-closing-brace-empty-line-before": "never",
|
'selector-attribute-brackets-space-inside': 'never',
|
||||||
"block-closing-brace-newline-after": "always",
|
'selector-attribute-operator-space-after': 'never',
|
||||||
"block-closing-brace-newline-before": "always-multi-line",
|
'selector-attribute-operator-space-before': 'never',
|
||||||
"block-closing-brace-space-before": "always-single-line",
|
'selector-combinator-space-after': 'always',
|
||||||
"block-no-empty": true,
|
'selector-combinator-space-before': 'always',
|
||||||
"block-opening-brace-newline-after": "always-multi-line",
|
'selector-list-comma-newline-after': 'always',
|
||||||
"block-opening-brace-space-after": "always-single-line",
|
'selector-list-comma-space-before': 'never',
|
||||||
"block-opening-brace-space-before": "always",
|
'selector-max-empty-lines': 0,
|
||||||
"color-hex-case": "lower",
|
'selector-nested-pattern': '^(?!&-|&_).*',
|
||||||
"color-hex-length": "short",
|
'selector-pseudo-class-case': 'lower',
|
||||||
"color-no-invalid-hex": true,
|
'selector-pseudo-class-no-unknown': true,
|
||||||
"comment-no-empty": true,
|
'selector-pseudo-class-parentheses-space-inside': 'never',
|
||||||
"comment-whitespace-inside": "always",
|
'selector-pseudo-element-case': 'lower',
|
||||||
"declaration-bang-space-after": "never",
|
'selector-pseudo-element-colon-notation': 'single',
|
||||||
"declaration-bang-space-before": "always",
|
'selector-pseudo-element-no-unknown': true,
|
||||||
"declaration-block-no-duplicate-properties": [true, {
|
'selector-type-case': 'lower',
|
||||||
ignore: ["consecutive-duplicates-with-different-values"],
|
'shorthand-property-no-redundant-values': true,
|
||||||
}],
|
'string-no-newline': true,
|
||||||
"declaration-block-no-redundant-longhand-properties": [true, {
|
'string-quotes': 'single',
|
||||||
ignoreShorthands: [/flex/, /grid/]
|
'unit-case': 'lower',
|
||||||
}],
|
'unit-no-unknown': true,
|
||||||
"declaration-block-semicolon-newline-after": "always-multi-line",
|
'value-list-comma-newline-after': 'always-multi-line',
|
||||||
"declaration-block-semicolon-space-after": "always-single-line",
|
'value-list-comma-space-after': 'always-single-line',
|
||||||
"declaration-block-semicolon-space-before": "never",
|
'value-list-comma-space-before': 'never',
|
||||||
"declaration-block-single-line-max-declarations": 1,
|
'value-list-max-empty-lines': 0,
|
||||||
"declaration-colon-newline-after": "always-multi-line",
|
|
||||||
"declaration-colon-space-after": "always-single-line",
|
|
||||||
"declaration-colon-space-before": "never",
|
|
||||||
"declaration-empty-line-before": "never",
|
|
||||||
"font-family-no-duplicate-names": true,
|
|
||||||
"function-comma-space-after": "always-single-line",
|
|
||||||
"function-comma-space-before": "never",
|
|
||||||
"function-max-empty-lines": 0,
|
|
||||||
"function-name-case": "lower",
|
|
||||||
"function-parentheses-newline-inside": "always-multi-line",
|
|
||||||
"function-parentheses-space-inside": "never-single-line",
|
|
||||||
"function-url-quotes": "always",
|
|
||||||
"function-whitespace-after": "always",
|
|
||||||
"indentation": 2,
|
|
||||||
"keyframe-declaration-no-important": true,
|
|
||||||
"length-zero-no-unit": true,
|
|
||||||
"max-empty-lines": 1,
|
|
||||||
"media-feature-colon-space-after": "always",
|
|
||||||
"media-feature-colon-space-before": "never",
|
|
||||||
"media-feature-name-case": "lower",
|
|
||||||
"media-feature-name-no-unknown": true,
|
|
||||||
"media-feature-parentheses-space-inside": "never",
|
|
||||||
"media-feature-range-operator-space-after": "always",
|
|
||||||
"media-query-list-comma-newline-after": "always-multi-line",
|
|
||||||
"media-query-list-comma-space-after": "always-single-line",
|
|
||||||
"media-query-list-comma-space-before": "never",
|
|
||||||
"no-duplicate-selectors": true,
|
|
||||||
"no-eol-whitespace": true,
|
|
||||||
"no-extra-semicolons": true,
|
|
||||||
"no-missing-end-of-source-newline": true,
|
|
||||||
"number-leading-zero": "never",
|
|
||||||
"number-no-trailing-zeros": true,
|
|
||||||
"order/properties-alphabetical-order": true,
|
|
||||||
"property-case": "lower",
|
|
||||||
"property-no-unknown": true,
|
|
||||||
"rule-empty-line-before": ["always-multi-line", {
|
|
||||||
except: ["first-nested"],
|
|
||||||
ignore: ["after-comment"]
|
|
||||||
}],
|
|
||||||
"scss/at-rule-no-unknown": true,
|
|
||||||
"scss/dollar-variable-colon-space-after": "always",
|
|
||||||
"scss/dollar-variable-colon-space-before": "never",
|
|
||||||
"scss/operator-no-newline-after": true,
|
|
||||||
"scss/operator-no-newline-before": true,
|
|
||||||
"scss/operator-no-unspaced": true,
|
|
||||||
"scss/selector-no-redundant-nesting-selector": true,
|
|
||||||
"selector-attribute-brackets-space-inside": "never",
|
|
||||||
"selector-attribute-operator-space-after": "never",
|
|
||||||
"selector-attribute-operator-space-before": "never",
|
|
||||||
"selector-combinator-space-after": "always",
|
|
||||||
"selector-combinator-space-before": "always",
|
|
||||||
"selector-list-comma-newline-after": "always",
|
|
||||||
"selector-list-comma-space-before": "never",
|
|
||||||
"selector-max-empty-lines": 0,
|
|
||||||
"selector-nested-pattern": "^(?!&-|&_).*",
|
|
||||||
"selector-pseudo-class-case": "lower",
|
|
||||||
"selector-pseudo-class-no-unknown": true,
|
|
||||||
"selector-pseudo-class-parentheses-space-inside": "never",
|
|
||||||
"selector-pseudo-element-case": "lower",
|
|
||||||
"selector-pseudo-element-colon-notation": "single",
|
|
||||||
"selector-pseudo-element-no-unknown": true,
|
|
||||||
"selector-type-case": "lower",
|
|
||||||
"shorthand-property-no-redundant-values": true,
|
|
||||||
"string-no-newline": true,
|
|
||||||
"string-quotes": "single",
|
|
||||||
"unit-case": "lower",
|
|
||||||
"unit-no-unknown": true,
|
|
||||||
"value-list-comma-newline-after": "always-multi-line",
|
|
||||||
"value-list-comma-space-after": "always-single-line",
|
|
||||||
"value-list-comma-space-before": "never",
|
|
||||||
"value-list-max-empty-lines": 0,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# MailPoet
|
# MailPoet
|
||||||
|
|
||||||
The **MailPoet** plugin.
|
The **MailPoet** plugin.
|
||||||
|
|
||||||
To use the official Docker-based development environment, see details
|
To use the official Docker-based development environment, see details
|
||||||
@ -16,11 +17,13 @@ below.
|
|||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- PHP >= 7.3 (only for the development environment, to run the plugin PHP >= 7.2 is required)
|
- PHP >= 7.3 (only for the development environment, to run the plugin PHP >= 7.2 is required)
|
||||||
- NodeJS
|
- NodeJS
|
||||||
- WordPress
|
- WordPress
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
The instructions below assume you already have a working WordPress development environment:
|
The instructions below assume you already have a working WordPress development environment:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -171,8 +174,7 @@ _x('text to translate', 'context for translators', 'mailpoet');
|
|||||||
**in Twig views**
|
**in Twig views**
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<%= __('text to translate') %>
|
<%= __('text to translate') %> <%= _n('single text', 'plural text', $number) %>
|
||||||
<%= _n('single text', 'plural text', $number) %>
|
|
||||||
<%= _x('text to translate', 'context for translators') %>
|
<%= _x('text to translate', 'context for translators') %>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -183,12 +185,8 @@ The domain `mailpoet` will be added automatically by the Twig functions.
|
|||||||
First add the string to the translations block in the Twig view:
|
First add the string to the translations block in the Twig view:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<% block translations %>
|
<% block translations %> <%= localize({ 'key': __('string to translate'), ... })
|
||||||
<%= localize({
|
%> <% endblock %>
|
||||||
'key': __('string to translate'),
|
|
||||||
...
|
|
||||||
}) %>
|
|
||||||
<% endblock %>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then use `MailPoet.I18n.t('key')` to get the translated string on your Javascript code.
|
Then use `MailPoet.I18n.t('key')` to get the translated string on your Javascript code.
|
||||||
@ -197,9 +195,11 @@ Then use `MailPoet.I18n.t('key')` to get the translated string on your Javascrip
|
|||||||
|
|
||||||
To run the whole acceptance testing suite you need the docker daemon to be running and after that use a command: `./do test:acceptance`.
|
To run the whole acceptance testing suite you need the docker daemon to be running and after that use a command: `./do test:acceptance`.
|
||||||
If you want to run only a single test use the parameter `--file`:
|
If you want to run only a single test use the parameter `--file`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./do test:acceptance --skip-deps --file tests/acceptance/ReceiveStandardEmailCest.php
|
./do test:acceptance --skip-deps --file tests/acceptance/ReceiveStandardEmailCest.php
|
||||||
```
|
```
|
||||||
|
|
||||||
The argument `--skip-deps` is useful locally to speed up the run.
|
The argument `--skip-deps` is useful locally to speed up the run.
|
||||||
|
|
||||||
If there are some unexpected errors you can delete all the runtime and start again.
|
If there are some unexpected errors you can delete all the runtime and start again.
|
||||||
|
File diff suppressed because one or more lines are too long
@ -2,7 +2,5 @@ import jQuery from 'jquery';
|
|||||||
|
|
||||||
jQuery(function adminDomReady($) {
|
jQuery(function adminDomReady($) {
|
||||||
// dom ready
|
// dom ready
|
||||||
$(function domReady() {
|
$(function domReady() {});
|
||||||
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -15,9 +15,11 @@ export type ErrorResponse = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export const isErrorResponse = (error: any): error is ErrorResponse => (
|
export const isErrorResponse = (error: any): error is ErrorResponse =>
|
||||||
error && typeof error === 'object' && 'errors' in error && Array.isArray(error.errors)
|
error &&
|
||||||
);
|
typeof error === 'object' &&
|
||||||
|
'errors' in error &&
|
||||||
|
Array.isArray(error.errors);
|
||||||
|
|
||||||
type ResponseType = JQuery.Deferred<Response, ErrorResponse>;
|
type ResponseType = JQuery.Deferred<Response, ErrorResponse>;
|
||||||
|
|
||||||
@ -94,7 +96,9 @@ export const MailPoetAjax = {
|
|||||||
},
|
},
|
||||||
constructGetUrl: function constructGetUrl(options): string {
|
constructGetUrl: function constructGetUrl(options): string {
|
||||||
this.init(options);
|
this.init(options);
|
||||||
return `${this.options.url as string}?${jQuery.param(this.getParams() as object)}`;
|
return `${this.options.url as string}?${jQuery.param(
|
||||||
|
this.getParams() as object,
|
||||||
|
)}`;
|
||||||
},
|
},
|
||||||
request: function request(method, options): ResponseType {
|
request: function request(method, options): ResponseType {
|
||||||
// set options
|
// set options
|
||||||
@ -117,15 +121,26 @@ export const MailPoetAjax = {
|
|||||||
success: null,
|
success: null,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
timeout: this.options.timeout,
|
timeout: this.options.timeout,
|
||||||
}).then((data: Response) => deferred.resolve(data), (failedXhr, textStatus) => {
|
}).then(
|
||||||
|
(data: Response) => deferred.resolve(data),
|
||||||
|
(failedXhr, textStatus) => {
|
||||||
let errorData: ErrorResponse;
|
let errorData: ErrorResponse;
|
||||||
if (textStatus === 'timeout') {
|
if (textStatus === 'timeout') {
|
||||||
errorData = buildErrorResponse(MailPoetI18n.t('ajaxTimeoutErrorMessage').replace('%d', timeout.toString()));
|
errorData = buildErrorResponse(
|
||||||
|
MailPoetI18n.t('ajaxTimeoutErrorMessage').replace(
|
||||||
|
'%d',
|
||||||
|
timeout.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
errorData = requestFailed(MailPoetI18n.t('ajaxFailedErrorMessage'), failedXhr);
|
errorData = requestFailed(
|
||||||
|
MailPoetI18n.t('ajaxFailedErrorMessage'),
|
||||||
|
failedXhr,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
void deferred.reject(errorData);
|
void deferred.reject(errorData);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// clear options
|
// clear options
|
||||||
this.options = {};
|
this.options = {};
|
||||||
|
@ -39,7 +39,10 @@ function track(name, data = []) {
|
|||||||
function exportMixpanel() {
|
function exportMixpanel() {
|
||||||
window.MailPoet.forceTrackEvent = track;
|
window.MailPoet.forceTrackEvent = track;
|
||||||
|
|
||||||
if (window.mailpoet_analytics_enabled && window.MailPoet.libs3rdPartyEnabled) {
|
if (
|
||||||
|
window.mailpoet_analytics_enabled &&
|
||||||
|
window.MailPoet.libs3rdPartyEnabled
|
||||||
|
) {
|
||||||
window.MailPoet.trackEvent = track;
|
window.MailPoet.trackEvent = track;
|
||||||
} else {
|
} else {
|
||||||
window.MailPoet.trackEvent = function emptyFunction() {};
|
window.MailPoet.trackEvent = function emptyFunction() {};
|
||||||
|
@ -30,7 +30,10 @@ export const withFeatureAnnouncement = <P extends Record<string, unknown>>(
|
|||||||
let beamerCallback;
|
let beamerCallback;
|
||||||
|
|
||||||
function showPluginUpdateNotice() {
|
function showPluginUpdateNotice() {
|
||||||
if (!window.mailpoet_update_available || document.getElementById('mailpoet_update_notice')) {
|
if (
|
||||||
|
!window.mailpoet_update_available ||
|
||||||
|
document.getElementById('mailpoet_update_notice')
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const updateMailPoetNotice = ReactStringReplace(
|
const updateMailPoetNotice = ReactStringReplace(
|
||||||
@ -99,7 +102,7 @@ export const withFeatureAnnouncement = <P extends Record<string, unknown>>(
|
|||||||
}: Omit<P, 'hasNews' | 'onBeamerClick'>) {
|
}: Omit<P, 'hasNews' | 'onBeamerClick'>) {
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
{...props as P}
|
{...(props as P)}
|
||||||
onBeamerClick={(e) => showBeamer(e)}
|
onBeamerClick={(e) => showBeamer(e)}
|
||||||
hasNews={showDot}
|
hasNews={showDot}
|
||||||
/>
|
/>
|
||||||
|
@ -3,9 +3,10 @@ import { api } from './config';
|
|||||||
|
|
||||||
const API_URL = `${api.root}/mailpoet/v1/automation`;
|
const API_URL = `${api.root}/mailpoet/v1/automation`;
|
||||||
|
|
||||||
export const request = (path: string, init?: RequestInit): ReturnType<typeof fetch> => (
|
export const request = (
|
||||||
fetch(`${API_URL}/${path}`, init)
|
path: string,
|
||||||
);
|
init?: RequestInit,
|
||||||
|
): ReturnType<typeof fetch> => fetch(`${API_URL}/${path}`, init);
|
||||||
|
|
||||||
type Error<T> = {
|
type Error<T> = {
|
||||||
response?: Response;
|
response?: Response;
|
||||||
@ -18,26 +19,25 @@ type State<T> = {
|
|||||||
error?: Error<T>;
|
error?: Error<T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Result<T> = [
|
type Result<T> = [(init?: RequestInit) => Promise<void>, State<T>];
|
||||||
(init?: RequestInit) => Promise<void>,
|
|
||||||
State<T>,
|
|
||||||
];
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
type Data = Record<string, any>;
|
type Data = Record<string, any>;
|
||||||
|
|
||||||
export const useMutation = <T extends Data>(path: string, config?: RequestInit): Result<T> => {
|
export const useMutation = <T extends Data>(
|
||||||
|
path: string,
|
||||||
|
config?: RequestInit,
|
||||||
|
): Result<T> => {
|
||||||
const [state, setState] = useState<State<T>>({
|
const [state, setState] = useState<State<T>>({
|
||||||
data: undefined,
|
data: undefined,
|
||||||
loading: false,
|
loading: false,
|
||||||
error: undefined,
|
error: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mutation = useCallback(async (init?: RequestInit) => {
|
const mutation = useCallback(
|
||||||
|
async (init?: RequestInit) => {
|
||||||
setState((prevState) => ({ ...prevState, loading: true }));
|
setState((prevState) => ({ ...prevState, loading: true }));
|
||||||
const response = await request(
|
const response = await request(path, {
|
||||||
path,
|
|
||||||
{
|
|
||||||
...config,
|
...config,
|
||||||
...init,
|
...init,
|
||||||
headers: {
|
headers: {
|
||||||
@ -45,8 +45,7 @@ export const useMutation = <T extends Data>(path: string, config?: RequestInit):
|
|||||||
...(init?.headers ?? {}),
|
...(init?.headers ?? {}),
|
||||||
'x-wp-nonce': api.nonce,
|
'x-wp-nonce': api.nonce,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@ -58,19 +57,24 @@ export const useMutation = <T extends Data>(path: string, config?: RequestInit):
|
|||||||
} finally {
|
} finally {
|
||||||
setState((prevState) => ({ ...prevState, loading: false }));
|
setState((prevState) => ({ ...prevState, loading: false }));
|
||||||
}
|
}
|
||||||
}, [config, path]);
|
},
|
||||||
|
[config, path],
|
||||||
|
);
|
||||||
|
|
||||||
return [mutation, state];
|
return [mutation, state];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useQuery = <T extends Data>(path: string, init?: RequestInit): State<T> => {
|
export const useQuery = <T extends Data>(
|
||||||
|
path: string,
|
||||||
|
init?: RequestInit,
|
||||||
|
): State<T> => {
|
||||||
const [mutation, result] = useMutation<T>(path, init);
|
const [mutation, result] = useMutation<T>(path, init);
|
||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => {
|
() => {
|
||||||
void mutation();
|
void mutation();
|
||||||
},
|
},
|
||||||
[], /* eslint-disable-line react-hooks/exhaustive-deps -- request only on initial load */
|
[] /* eslint-disable-line react-hooks/exhaustive-deps -- request only on initial load */,
|
||||||
);
|
);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -13,24 +13,26 @@ function ApiCheck(): JSX.Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function RecreateSchemaButton(): JSX.Element {
|
function RecreateSchemaButton(): JSX.Element {
|
||||||
const [createSchema, { loading, error }] = useMutation('system/database', { method: 'POST' });
|
const [createSchema, { loading, error }] = useMutation('system/database', {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button type="button" onClick={() => createSchema()} disabled={loading}>
|
||||||
type="button"
|
|
||||||
onClick={() => createSchema()}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
Recreate DB schema (data will be lost)
|
Recreate DB schema (data will be lost)
|
||||||
</button>
|
</button>
|
||||||
{error && (<div>{error?.data?.message ?? 'An unknown error occurred'}</div>)}
|
{error && (
|
||||||
|
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DeleteSchemaButton(): JSX.Element {
|
function DeleteSchemaButton(): JSX.Element {
|
||||||
const [deleteSchema, { loading, error }] = useMutation('system/database', { method: 'DELETE' });
|
const [deleteSchema, { loading, error }] = useMutation('system/database', {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -38,13 +40,16 @@ function DeleteSchemaButton(): JSX.Element {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await deleteSchema();
|
await deleteSchema();
|
||||||
window.location.href = '/wp-admin/admin.php?page=mailpoet-experimental';
|
window.location.href =
|
||||||
|
'/wp-admin/admin.php?page=mailpoet-experimental';
|
||||||
}}
|
}}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
Delete DB schema & deactivate feature
|
Delete DB schema & deactivate feature
|
||||||
</button>
|
</button>
|
||||||
{error && (<div>{error?.data?.message ?? 'An unknown error occurred'}</div>)}
|
{error && (
|
||||||
|
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,20 +30,26 @@ const createWorkflow = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function CreateTestingWorkflowButton(): JSX.Element {
|
export function CreateTestingWorkflowButton(): JSX.Element {
|
||||||
const [createSchema, { loading, error }] = useMutation('workflows', { method: 'POST' });
|
const [createSchema, { loading, error }] = useMutation('workflows', {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => createSchema({
|
onClick={() =>
|
||||||
|
createSchema({
|
||||||
body: JSON.stringify(createWorkflow()),
|
body: JSON.stringify(createWorkflow()),
|
||||||
})}
|
})
|
||||||
|
}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
Create testing workflow
|
Create testing workflow
|
||||||
</button>
|
</button>
|
||||||
{error && (<div>{error?.data?.message ?? 'An unknown error occurred'}</div>)}
|
{error && (
|
||||||
|
<div>{error?.data?.message ?? 'An unknown error occurred'}</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,7 @@ export function WithoutIcons() {
|
|||||||
<>
|
<>
|
||||||
<Heading level={3}>Small buttons</Heading>
|
<Heading level={3}>Small buttons</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button
|
<Button onClick={action('primary small')} dimension="small">
|
||||||
onClick={action('primary small')}
|
|
||||||
dimension="small"
|
|
||||||
>
|
|
||||||
Primary button
|
Primary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -44,27 +41,14 @@ export function WithoutIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Regular buttons</Heading>
|
<Heading level={3}>Regular buttons</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button
|
<Button onClick={action('primary regular')}>Primary button</Button>
|
||||||
onClick={action('primary regular')}
|
<Button onClick={action('secondary regular')} variant="secondary">
|
||||||
>
|
|
||||||
Primary button
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={action('secondary regular')}
|
|
||||||
variant="secondary"
|
|
||||||
>
|
|
||||||
Secondary button
|
Secondary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={action('tertiary regular')} variant="tertiary">
|
||||||
onClick={action('tertiary regular')}
|
|
||||||
variant="tertiary"
|
|
||||||
>
|
|
||||||
Tertiary button
|
Tertiary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={action('destructive regular')} variant="destructive">
|
||||||
onClick={action('destructive regular')}
|
|
||||||
variant="destructive"
|
|
||||||
>
|
|
||||||
Destructive button
|
Destructive button
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
@ -72,25 +56,14 @@ export function WithoutIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Disabled buttons</Heading>
|
<Heading level={3}>Disabled buttons</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button isDisabled>
|
<Button isDisabled>Primary button</Button>
|
||||||
Primary button
|
<Button isDisabled variant="secondary">
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
isDisabled
|
|
||||||
variant="secondary"
|
|
||||||
>
|
|
||||||
Secondary button
|
Secondary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button isDisabled variant="tertiary">
|
||||||
isDisabled
|
|
||||||
variant="tertiary"
|
|
||||||
>
|
|
||||||
Tertiary button
|
Tertiary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button isDisabled variant="destructive">
|
||||||
isDisabled
|
|
||||||
variant="destructive"
|
|
||||||
>
|
|
||||||
Destructive button
|
Destructive button
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
@ -98,25 +71,14 @@ export function WithoutIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Buttons with spinner</Heading>
|
<Heading level={3}>Buttons with spinner</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button withSpinner>
|
<Button withSpinner>Primary button</Button>
|
||||||
Primary button
|
<Button withSpinner variant="secondary">
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
withSpinner
|
|
||||||
variant="secondary"
|
|
||||||
>
|
|
||||||
Secondary button
|
Secondary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button withSpinner variant="tertiary">
|
||||||
withSpinner
|
|
||||||
variant="tertiary"
|
|
||||||
>
|
|
||||||
Tertiary button
|
Tertiary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button withSpinner variant="destructive">
|
||||||
withSpinner
|
|
||||||
variant="destructive"
|
|
||||||
>
|
|
||||||
Destructive button
|
Destructive button
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
@ -124,10 +86,7 @@ export function WithoutIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Full width buttons</Heading>
|
<Heading level={3}>Full width buttons</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button
|
<Button onClick={action('primary full-width')} isFullWidth>
|
||||||
onClick={action('primary full-width')}
|
|
||||||
isFullWidth
|
|
||||||
>
|
|
||||||
Primary button
|
Primary button
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
@ -54,10 +54,7 @@ export function WithIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Regular buttons</Heading>
|
<Heading level={3}>Regular buttons</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button
|
<Button onClick={action('icon start primary regular')} iconStart={icon}>
|
||||||
onClick={action('icon start primary regular')}
|
|
||||||
iconStart={icon}
|
|
||||||
>
|
|
||||||
Icon start
|
Icon start
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@ -90,35 +87,15 @@ export function WithIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Disabled buttons</Heading>
|
<Heading level={3}>Disabled buttons</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button
|
<Button isDisabled iconStart={icon}>
|
||||||
isDisabled
|
|
||||||
iconStart={icon}
|
|
||||||
>
|
|
||||||
Icon start
|
Icon start
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button isDisabled variant="secondary" iconStart={icon} iconEnd={icon}>
|
||||||
isDisabled
|
|
||||||
variant="secondary"
|
|
||||||
iconStart={icon}
|
|
||||||
iconEnd={icon}
|
|
||||||
>
|
|
||||||
Both icons
|
Both icons
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button isDisabled variant="secondary" iconStart={icon} />
|
||||||
isDisabled
|
<Button isDisabled variant="tertiary" iconStart={icon} />
|
||||||
variant="secondary"
|
<Button isDisabled variant="destructive" iconEnd={icon}>
|
||||||
iconStart={icon}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
isDisabled
|
|
||||||
variant="tertiary"
|
|
||||||
iconStart={icon}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
isDisabled
|
|
||||||
variant="destructive"
|
|
||||||
iconEnd={icon}
|
|
||||||
>
|
|
||||||
Icon end
|
Icon end
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
@ -126,35 +103,15 @@ export function WithIcons() {
|
|||||||
|
|
||||||
<Heading level={3}>Buttons with spinner</Heading>
|
<Heading level={3}>Buttons with spinner</Heading>
|
||||||
<p>
|
<p>
|
||||||
<Button
|
<Button withSpinner iconStart={icon}>
|
||||||
withSpinner
|
|
||||||
iconStart={icon}
|
|
||||||
>
|
|
||||||
Icon start
|
Icon start
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button withSpinner variant="secondary" iconStart={icon} iconEnd={icon}>
|
||||||
withSpinner
|
|
||||||
variant="secondary"
|
|
||||||
iconStart={icon}
|
|
||||||
iconEnd={icon}
|
|
||||||
>
|
|
||||||
Both icons
|
Both icons
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button withSpinner variant="secondary" iconStart={icon} />
|
||||||
withSpinner
|
<Button withSpinner variant="tertiary" iconStart={icon} />
|
||||||
variant="secondary"
|
<Button withSpinner variant="destructive" iconEnd={icon}>
|
||||||
iconStart={icon}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
withSpinner
|
|
||||||
variant="tertiary"
|
|
||||||
iconStart={icon}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
withSpinner
|
|
||||||
variant="destructive"
|
|
||||||
iconEnd={icon}
|
|
||||||
>
|
|
||||||
Icon end
|
Icon end
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</p>
|
||||||
|
@ -45,12 +45,7 @@ function Button({
|
|||||||
target={target}
|
target={target}
|
||||||
rel={rel}
|
rel={rel}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
className={
|
className={classnames(className, 'button', 'mailpoet-button', {
|
||||||
classnames(
|
|
||||||
className,
|
|
||||||
'button',
|
|
||||||
'mailpoet-button',
|
|
||||||
{
|
|
||||||
'mailpoet-button-with-spinner': withSpinner,
|
'mailpoet-button-with-spinner': withSpinner,
|
||||||
'mailpoet-button-disabled': isDisabled,
|
'mailpoet-button-disabled': isDisabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
@ -59,9 +54,7 @@ function Button({
|
|||||||
'button-link': variant === 'tertiary',
|
'button-link': variant === 'tertiary',
|
||||||
'button-link button-link-delete': variant === 'destructive',
|
'button-link button-link-delete': variant === 'destructive',
|
||||||
'button-small': dimension === 'small',
|
'button-small': dimension === 'small',
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
data-automation-id={automationId}
|
data-automation-id={automationId}
|
||||||
>
|
>
|
||||||
{iconStart}
|
{iconStart}
|
||||||
|
@ -17,9 +17,21 @@ export function CategoriesWithCount() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Categories onSelect={noop} categories={categories} active={categories[0].name} />
|
<Categories
|
||||||
<Categories onSelect={noop} categories={categories} active={categories[2].name} />
|
onSelect={noop}
|
||||||
<Categories onSelect={noop} categories={categories} active={categories[4].name} />
|
categories={categories}
|
||||||
|
active={categories[0].name}
|
||||||
|
/>
|
||||||
|
<Categories
|
||||||
|
onSelect={noop}
|
||||||
|
categories={categories}
|
||||||
|
active={categories[2].name}
|
||||||
|
/>
|
||||||
|
<Categories
|
||||||
|
onSelect={noop}
|
||||||
|
categories={categories}
|
||||||
|
active={categories[4].name}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -35,9 +47,21 @@ export function CategoriesWithoutCount() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Categories onSelect={noop} categories={categories} active={categories[0].name} />
|
<Categories
|
||||||
<Categories onSelect={noop} categories={categories} active={categories[2].name} />
|
onSelect={noop}
|
||||||
<Categories onSelect={noop} categories={categories} active={categories[4].name} />
|
categories={categories}
|
||||||
|
active={categories[0].name}
|
||||||
|
/>
|
||||||
|
<Categories
|
||||||
|
onSelect={noop}
|
||||||
|
categories={categories}
|
||||||
|
active={categories[2].name}
|
||||||
|
/>
|
||||||
|
<Categories
|
||||||
|
onSelect={noop}
|
||||||
|
categories={categories}
|
||||||
|
active={categories[4].name}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,7 @@ function Categories({ onSelect, categories, active }: Props) {
|
|||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
||||||
return (
|
return <div className="mailpoet-categories">{cats}</div>;
|
||||||
<div className="mailpoet-categories">
|
|
||||||
{ cats }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Categories;
|
export default Categories;
|
||||||
|
@ -20,10 +20,7 @@ function CategoriesItem({
|
|||||||
automationId,
|
automationId,
|
||||||
active,
|
active,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const classes = classNames(
|
const classes = classNames('mailpoet-categories-item', { active: !!active });
|
||||||
'mailpoet-categories-item',
|
|
||||||
{ active: !!active },
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
@ -41,7 +38,7 @@ function CategoriesItem({
|
|||||||
</span>
|
</span>
|
||||||
{count > 0 && (
|
{count > 0 && (
|
||||||
<span className="mailpoet-categories-count">
|
<span className="mailpoet-categories-count">
|
||||||
{ parseInt(count.toString(), 10).toLocaleString() }
|
{parseInt(count.toString(), 10).toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</a>
|
</a>
|
||||||
|
@ -8,13 +8,21 @@ function ConfirmAlert(props) {
|
|||||||
template: ReactDOMServer.renderToString(
|
template: ReactDOMServer.renderToString(
|
||||||
<>
|
<>
|
||||||
<p>{props.message}</p>
|
<p>{props.message}</p>
|
||||||
<button id="mailpoet_alert_cancel" className="button button-secondary" type="button">
|
<button
|
||||||
|
id="mailpoet_alert_cancel"
|
||||||
|
className="button button-secondary"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
{props.cancelLabel}
|
{props.cancelLabel}
|
||||||
</button>
|
</button>
|
||||||
<button id="mailpoet_alert_confirm" className="button button-primary" type="submit">
|
<button
|
||||||
|
id="mailpoet_alert_confirm"
|
||||||
|
className="button button-primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{props.confirmLabel}
|
{props.confirmLabel}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</>,
|
||||||
),
|
),
|
||||||
onInit: () => {
|
onInit: () => {
|
||||||
document
|
document
|
||||||
@ -55,6 +63,6 @@ export default function confirmAlert(props) {
|
|||||||
cancelLabel={props.cancelLabel}
|
cancelLabel={props.cancelLabel}
|
||||||
confirmLabel={props.confirmLabel}
|
confirmLabel={props.confirmLabel}
|
||||||
onConfirm={props.onConfirm}
|
onConfirm={props.onConfirm}
|
||||||
/>
|
/>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,9 @@ export default async function callApi({ endpoint, action, data }) {
|
|||||||
});
|
});
|
||||||
return { success: true, res };
|
return { success: true, res };
|
||||||
} catch (res) {
|
} catch (res) {
|
||||||
const error = isErrorResponse(res) ? res.errors.map((e) => e.message) : null;
|
const error = isErrorResponse(res)
|
||||||
|
? res.errors.map((e) => e.message)
|
||||||
|
: null;
|
||||||
return { success: false, error, res };
|
return { success: false, error, res };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
|
|
||||||
const sleep = (ms:number) => new Promise((resolve) => { setTimeout(resolve, ms); });
|
const sleep = (ms: number) =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
setTimeout(resolve, ms);
|
||||||
|
});
|
||||||
|
|
||||||
export default async function trackEvent(
|
export default async function trackEvent({
|
||||||
{
|
|
||||||
name,
|
name,
|
||||||
data,
|
data,
|
||||||
timeout = 0,
|
timeout = 0,
|
||||||
}:{ name:string, data:object, timeout:number },
|
}: {
|
||||||
) {
|
name: string;
|
||||||
|
data: object;
|
||||||
|
timeout: number;
|
||||||
|
}) {
|
||||||
MailPoet.trackEvent(name, data);
|
MailPoet.trackEvent(name, data);
|
||||||
return sleep(timeout);
|
return sleep(timeout);
|
||||||
}
|
}
|
||||||
|
@ -9,21 +9,13 @@ export default {
|
|||||||
component: Datepicker,
|
component: Datepicker,
|
||||||
};
|
};
|
||||||
|
|
||||||
function DatepickerWrapper({
|
function DatepickerWrapper({ ...props }) {
|
||||||
...props
|
|
||||||
}) {
|
|
||||||
const [startDate, setStartDate] = useState(new Date());
|
const [startDate, setStartDate] = useState(new Date());
|
||||||
const onChange = (date:Date) => {
|
const onChange = (date: Date) => {
|
||||||
props.onChange(date);
|
props.onChange(date);
|
||||||
setStartDate(date);
|
setStartDate(date);
|
||||||
};
|
};
|
||||||
return (
|
return <Datepicker {...props} selected={startDate} onChange={onChange} />;
|
||||||
<Datepicker
|
|
||||||
{...props}
|
|
||||||
selected={startDate}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Datepickers() {
|
export function Datepickers() {
|
||||||
|
@ -18,16 +18,11 @@ function Datepicker({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames('mailpoet-datepicker mailpoet-form-input', {
|
||||||
classnames(
|
|
||||||
'mailpoet-datepicker mailpoet-form-input',
|
|
||||||
{
|
|
||||||
[`mailpoet-form-input-${dimension}`]: dimension,
|
[`mailpoet-form-input-${dimension}`]: dimension,
|
||||||
'mailpoet-disabled': props.disabled,
|
'mailpoet-disabled': props.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{iconStart}
|
{iconStart}
|
||||||
<ReactDatePicker
|
<ReactDatePicker
|
||||||
|
@ -17,13 +17,11 @@ function Checkbox({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={
|
className={classnames({
|
||||||
classnames({
|
|
||||||
'mailpoet-form-checkbox': true,
|
'mailpoet-form-checkbox': true,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
data-automation-id={automationId}
|
data-automation-id={automationId}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
@ -26,7 +26,7 @@ function CheckboxGroup({
|
|||||||
|
|
||||||
const handleChange = (value: CheckboxValueType, isChecked: boolean) => {
|
const handleChange = (value: CheckboxValueType, isChecked: boolean) => {
|
||||||
const index = values.indexOf(value);
|
const index = values.indexOf(value);
|
||||||
let newValues:CheckboxValueType[] = [];
|
let newValues: CheckboxValueType[] = [];
|
||||||
if (isChecked && index === -1) {
|
if (isChecked && index === -1) {
|
||||||
newValues = values.concat([value]);
|
newValues = values.concat([value]);
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ function CheckboxGroup({
|
|||||||
<div>
|
<div>
|
||||||
{options.map((props: CheckboxProps) => {
|
{options.map((props: CheckboxProps) => {
|
||||||
const { label, ...attributes } = props;
|
const { label, ...attributes } = props;
|
||||||
const value = (props.value as CheckboxValueType);
|
const value = props.value as CheckboxValueType;
|
||||||
return (
|
return (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={values.includes(value)}
|
checked={values.includes(value)}
|
||||||
|
@ -12,11 +12,7 @@ export function Inputs() {
|
|||||||
<>
|
<>
|
||||||
<Heading level={3}>Small inputs</Heading>
|
<Heading level={3}>Small inputs</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input type="text" dimension="small" placeholder="Small input value" />
|
||||||
type="text"
|
|
||||||
dimension="small"
|
|
||||||
placeholder="Small input value"
|
|
||||||
/>
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
@ -43,10 +39,7 @@ export function Inputs() {
|
|||||||
<br />
|
<br />
|
||||||
<Heading level={3}>Regular inputs</Heading>
|
<Heading level={3}>Regular inputs</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input type="text" placeholder="Regular input" />
|
||||||
type="text"
|
|
||||||
placeholder="Regular input"
|
|
||||||
/>
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
@ -67,20 +60,12 @@ export function Inputs() {
|
|||||||
iconEnd={icon}
|
iconEnd={icon}
|
||||||
/>
|
/>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Input
|
<Input disabled type="text" placeholder="Disabled input" />
|
||||||
disabled
|
|
||||||
type="text"
|
|
||||||
placeholder="Disabled input"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<Heading level={3}>Full-width inputs</Heading>
|
<Heading level={3}>Full-width inputs</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input type="text" placeholder="Full-width input" isFullWidth />
|
||||||
type="text"
|
|
||||||
placeholder="Full-width input"
|
|
||||||
isFullWidth
|
|
||||||
/>
|
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Full-width input with iconStart"
|
placeholder="Full-width input with iconStart"
|
||||||
|
@ -23,25 +23,25 @@ function Input({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames(className, 'mailpoet-form-input', {
|
||||||
classnames(
|
|
||||||
className,
|
|
||||||
'mailpoet-form-input',
|
|
||||||
{
|
|
||||||
[`mailpoet-form-input-${dimension}`]: dimension,
|
[`mailpoet-form-input-${dimension}`]: dimension,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{iconStart}
|
{iconStart}
|
||||||
<input {...attributes} />
|
<input {...attributes} />
|
||||||
{customLabel && <div className="mailpoet-form-input-label">{customLabel}</div>}
|
{customLabel && (
|
||||||
|
<div className="mailpoet-form-input-label">{customLabel}</div>
|
||||||
|
)}
|
||||||
{tooltip && (
|
{tooltip && (
|
||||||
<>
|
<>
|
||||||
<span className="mailpoet-form-tooltip-holder">
|
<span className="mailpoet-form-tooltip-holder">
|
||||||
<span className="mailpoet-form-tooltip-icon" data-tip data-for={attributes.name} />
|
<span
|
||||||
|
className="mailpoet-form-tooltip-icon"
|
||||||
|
data-tip
|
||||||
|
data-for={attributes.name}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<Tooltip place="right" multiline id={attributes.name}>
|
<Tooltip place="right" multiline id={attributes.name}>
|
||||||
{tooltip}
|
{tooltip}
|
||||||
|
@ -23,18 +23,10 @@ export function Radios() {
|
|||||||
<>
|
<>
|
||||||
<Heading level={3}>Inline individual radios</Heading>
|
<Heading level={3}>Inline individual radios</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Radio
|
<Radio onCheck={action('radio-individual-1')} name="story" value="1">
|
||||||
onCheck={action('radio-individual-1')}
|
|
||||||
name="story"
|
|
||||||
value="1"
|
|
||||||
>
|
|
||||||
Option 1
|
Option 1
|
||||||
</Radio>
|
</Radio>
|
||||||
<Radio
|
<Radio onCheck={action('radio-individual-2')} name="story" value="2">
|
||||||
onCheck={action('radio-individual-2')}
|
|
||||||
name="story"
|
|
||||||
value="2"
|
|
||||||
>
|
|
||||||
Option 2
|
Option 2
|
||||||
</Radio>
|
</Radio>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,7 +33,7 @@ function RadioGroup({
|
|||||||
<div>
|
<div>
|
||||||
{options.map((props: RadioProps) => {
|
{options.map((props: RadioProps) => {
|
||||||
const { label, ...attributes } = props;
|
const { label, ...attributes } = props;
|
||||||
const value = (props.value as RadioValueType);
|
const value = props.value as RadioValueType;
|
||||||
return (
|
return (
|
||||||
<Radio
|
<Radio
|
||||||
checked={currentValue === value}
|
checked={currentValue === value}
|
||||||
|
@ -17,13 +17,11 @@ function Radio({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={
|
className={classnames({
|
||||||
classnames({
|
|
||||||
'mailpoet-form-radio': true,
|
'mailpoet-form-radio': true,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
data-automation-id={automationId}
|
data-automation-id={automationId}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
@ -26,7 +26,8 @@ export function ReactSelect() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'long',
|
value: 'long',
|
||||||
label: 'Very very very very very very very very very very very very long option',
|
label:
|
||||||
|
'Very very very very very very very very very very very very long option',
|
||||||
tag: 'long',
|
tag: 'long',
|
||||||
count: 1234,
|
count: 1234,
|
||||||
},
|
},
|
||||||
|
@ -19,9 +19,13 @@ type LabelRendererProps = {
|
|||||||
function LabelRenderer(data: LabelRendererProps) {
|
function LabelRenderer(data: LabelRendererProps) {
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-form-react-select-option">
|
<div className="mailpoet-form-react-select-option">
|
||||||
{data.tag && <span className="mailpoet-form-react-select-tag">{data.tag}</span>}
|
{data.tag && (
|
||||||
|
<span className="mailpoet-form-react-select-tag">{data.tag}</span>
|
||||||
|
)}
|
||||||
<span>{data.label}</span>
|
<span>{data.label}</span>
|
||||||
{data.count !== undefined && <span className="mailpoet-form-react-select-count">{data.count}</span>}
|
{data.count !== undefined && (
|
||||||
|
<span className="mailpoet-form-react-select-count">{data.count}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -43,14 +47,12 @@ function Option(props: OptionProps<OptionData>) {
|
|||||||
style={style}
|
style={style}
|
||||||
ref={props.innerRef}
|
ref={props.innerRef}
|
||||||
{...props.innerProps}
|
{...props.innerProps}
|
||||||
className={
|
className={classnames({
|
||||||
classnames({
|
|
||||||
'mailpoet-form-react-select__option': true,
|
'mailpoet-form-react-select__option': true,
|
||||||
'mailpoet-form-react-select__option--is-disabled': props.isDisabled,
|
'mailpoet-form-react-select__option--is-disabled': props.isDisabled,
|
||||||
'mailpoet-form-react-select__option--is-focused': props.isFocused,
|
'mailpoet-form-react-select__option--is-focused': props.isFocused,
|
||||||
'mailpoet-form-react-select__option--is-selected': props.isSelected,
|
'mailpoet-form-react-select__option--is-selected': props.isSelected,
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{LabelRenderer(props.data)}
|
{LabelRenderer(props.data)}
|
||||||
</div>
|
</div>
|
||||||
@ -62,12 +64,11 @@ function SingleValue(props: any) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...props.innerProps}
|
{...props.innerProps}
|
||||||
className={
|
className={classnames({
|
||||||
classnames({
|
|
||||||
'mailpoet-form-react-select__single-value': true,
|
'mailpoet-form-react-select__single-value': true,
|
||||||
'mailpoet-form-react-select__single-value--is-disabled': props.isDisabled,
|
'mailpoet-form-react-select__single-value--is-disabled':
|
||||||
})
|
props.isDisabled,
|
||||||
}
|
})}
|
||||||
>
|
>
|
||||||
{LabelRenderer(props.data as LabelRendererProps)}
|
{LabelRenderer(props.data as LabelRendererProps)}
|
||||||
</div>
|
</div>
|
||||||
@ -105,17 +106,11 @@ function ReactSelect({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames('mailpoet-form-input', 'mailpoet-form-select', {
|
||||||
classnames(
|
|
||||||
'mailpoet-form-input',
|
|
||||||
'mailpoet-form-select',
|
|
||||||
{
|
|
||||||
[`mailpoet-form-input-${dimension}`]: dimension,
|
[`mailpoet-form-input-${dimension}`]: dimension,
|
||||||
'mailpoet-disabled': props.disabled,
|
'mailpoet-disabled': props.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
data-automation-id={automationId}
|
data-automation-id={automationId}
|
||||||
>
|
>
|
||||||
{iconStart}
|
{iconStart}
|
||||||
|
@ -18,10 +18,7 @@ export function NativeSelect() {
|
|||||||
<option value="3">Opt 3</option>
|
<option value="3">Opt 3</option>
|
||||||
</Select>
|
</Select>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Select
|
<Select isMinWidth iconStart={icon}>
|
||||||
isMinWidth
|
|
||||||
iconStart={icon}
|
|
||||||
>
|
|
||||||
<option value="1">Opt 1</option>
|
<option value="1">Opt 1</option>
|
||||||
<option value="2">Opt 2</option>
|
<option value="2">Opt 2</option>
|
||||||
<option value="3">Opt 3</option>
|
<option value="3">Opt 3</option>
|
||||||
@ -36,10 +33,7 @@ export function NativeSelect() {
|
|||||||
<option value="3">Option 3</option>
|
<option value="3">Option 3</option>
|
||||||
</Select>
|
</Select>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Select
|
<Select dimension="small" iconStart={icon}>
|
||||||
dimension="small"
|
|
||||||
iconStart={icon}
|
|
||||||
>
|
|
||||||
<option value="1">Option 1</option>
|
<option value="1">Option 1</option>
|
||||||
<option value="2">Option 2</option>
|
<option value="2">Option 2</option>
|
||||||
<option value="3">Option 3</option>
|
<option value="3">Option 3</option>
|
||||||
@ -73,10 +67,7 @@ export function NativeSelect() {
|
|||||||
<option value="3">Option 3</option>
|
<option value="3">Option 3</option>
|
||||||
</Select>
|
</Select>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Select
|
<Select isFullWidth iconStart={icon}>
|
||||||
isFullWidth
|
|
||||||
iconStart={icon}
|
|
||||||
>
|
|
||||||
<option value="1">Option 1</option>
|
<option value="1">Option 1</option>
|
||||||
<option value="2">Option 2</option>
|
<option value="2">Option 2</option>
|
||||||
<option value="3">Option 3</option>
|
<option value="3">Option 3</option>
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import {
|
import { forwardRef, ReactNode, Ref, SelectHTMLAttributes } from 'react';
|
||||||
forwardRef,
|
|
||||||
ReactNode,
|
|
||||||
Ref,
|
|
||||||
SelectHTMLAttributes,
|
|
||||||
} from 'react';
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
type Props = SelectHTMLAttributes<HTMLSelectElement> & {
|
type Props = SelectHTMLAttributes<HTMLSelectElement> & {
|
||||||
@ -15,7 +10,9 @@ type Props = SelectHTMLAttributes<HTMLSelectElement> & {
|
|||||||
automationId?: string;
|
automationId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Select = forwardRef(({
|
const Select = forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
children,
|
children,
|
||||||
dimension,
|
dimension,
|
||||||
isFullWidth,
|
isFullWidth,
|
||||||
@ -23,26 +20,23 @@ const Select = forwardRef(({
|
|||||||
iconStart,
|
iconStart,
|
||||||
automationId,
|
automationId,
|
||||||
...attributes
|
...attributes
|
||||||
}: Props, ref?: Ref<HTMLSelectElement>) => (
|
}: Props,
|
||||||
|
ref?: Ref<HTMLSelectElement>,
|
||||||
|
) => (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames('mailpoet-form-input', 'mailpoet-form-select', {
|
||||||
classnames(
|
|
||||||
'mailpoet-form-input',
|
|
||||||
'mailpoet-form-select',
|
|
||||||
{
|
|
||||||
[`mailpoet-form-input-${dimension}`]: dimension,
|
[`mailpoet-form-input-${dimension}`]: dimension,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
'mailpoet-min-width': isMinWidth,
|
'mailpoet-min-width': isMinWidth,
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{iconStart}
|
{iconStart}
|
||||||
<select {...attributes} ref={ref} data-automation-id={automationId}>
|
<select {...attributes} ref={ref} data-automation-id={automationId}>
|
||||||
{children}
|
{children}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
));
|
),
|
||||||
|
);
|
||||||
|
|
||||||
export default Select;
|
export default Select;
|
||||||
|
@ -11,30 +11,19 @@ export function Textareas() {
|
|||||||
<>
|
<>
|
||||||
<Heading level={3}>Small textareas</Heading>
|
<Heading level={3}>Small textareas</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Textarea
|
<Textarea dimension="small" placeholder="Small textarea value" />
|
||||||
dimension="small"
|
|
||||||
placeholder="Small textarea value"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<Heading level={3}>Regular textareas</Heading>
|
<Heading level={3}>Regular textareas</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Textarea
|
<Textarea placeholder="Regular textarea" />
|
||||||
placeholder="Regular textarea"
|
|
||||||
/>
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Textarea
|
<Textarea disabled placeholder="Disabled textarea" />
|
||||||
disabled
|
|
||||||
placeholder="Disabled textarea"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<Heading level={3}>Full-width textareas</Heading>
|
<Heading level={3}>Full-width textareas</Heading>
|
||||||
<div>
|
<div>
|
||||||
<Textarea
|
<Textarea placeholder="Full-width textarea" isFullWidth />
|
||||||
placeholder="Full-width textarea"
|
|
||||||
isFullWidth
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
</>
|
</>
|
||||||
|
@ -21,24 +21,24 @@ function Textarea({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames(className, 'mailpoet-form-textarea', {
|
||||||
classnames(
|
|
||||||
className,
|
|
||||||
'mailpoet-form-textarea',
|
|
||||||
{
|
|
||||||
[`mailpoet-form-textarea-${dimension}`]: dimension,
|
[`mailpoet-form-textarea-${dimension}`]: dimension,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<textarea className={classnames({ code: isCode })} {...attributes} />
|
<textarea className={classnames({ code: isCode })} {...attributes} />
|
||||||
{customLabel && <div className="mailpoet-form-input-label">{customLabel}</div>}
|
{customLabel && (
|
||||||
|
<div className="mailpoet-form-input-label">{customLabel}</div>
|
||||||
|
)}
|
||||||
{tooltip && (
|
{tooltip && (
|
||||||
<>
|
<>
|
||||||
<span className="mailpoet-form-tooltip-holder">
|
<span className="mailpoet-form-tooltip-holder">
|
||||||
<span className="mailpoet-form-tooltip-icon" data-tip data-for={attributes.name} />
|
<span
|
||||||
|
className="mailpoet-form-tooltip-icon"
|
||||||
|
data-tip
|
||||||
|
data-for={attributes.name}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<Tooltip place="right" multiline id={attributes.name}>
|
<Tooltip place="right" multiline id={attributes.name}>
|
||||||
{tooltip}
|
{tooltip}
|
||||||
|
@ -15,11 +15,7 @@ export function Toggles() {
|
|||||||
<Grid.Column dimension="small">
|
<Grid.Column dimension="small">
|
||||||
<Grid.SpaceBetween>
|
<Grid.SpaceBetween>
|
||||||
<label htmlFor="toggle-1">Toggle regular</label>
|
<label htmlFor="toggle-1">Toggle regular</label>
|
||||||
<Toggle
|
<Toggle onCheck={action('toggle-1')} id="toggle-1" name="toggle-1" />
|
||||||
onCheck={action('toggle-1')}
|
|
||||||
id="toggle-1"
|
|
||||||
name="toggle-1"
|
|
||||||
/>
|
|
||||||
</Grid.SpaceBetween>
|
</Grid.SpaceBetween>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Grid.SpaceBetween>
|
<Grid.SpaceBetween>
|
||||||
|
@ -16,14 +16,12 @@ function Toggle({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
className={
|
className={classnames({
|
||||||
classnames({
|
|
||||||
[className]: className,
|
[className]: className,
|
||||||
'mailpoet-form-toggle': true,
|
'mailpoet-form-toggle': true,
|
||||||
[`mailpoet-form-toggle-${dimension}`]: dimension,
|
[`mailpoet-form-toggle-${dimension}`]: dimension,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
data-automation-id={automationId}
|
data-automation-id={automationId}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
@ -14,35 +14,18 @@ export function YesNos() {
|
|||||||
<Heading level={3}>YesNos</Heading>
|
<Heading level={3}>YesNos</Heading>
|
||||||
<Grid.Column dimension="small">
|
<Grid.Column dimension="small">
|
||||||
<Grid.SpaceBetween verticalAlign="center">
|
<Grid.SpaceBetween verticalAlign="center">
|
||||||
<div>
|
<div>YesNo</div>
|
||||||
YesNo
|
<YesNo onCheck={action('yesno-1')} name="yesno-1" />
|
||||||
</div>
|
|
||||||
<YesNo
|
|
||||||
onCheck={action('yesno-1')}
|
|
||||||
name="yesno-1"
|
|
||||||
/>
|
|
||||||
</Grid.SpaceBetween>
|
</Grid.SpaceBetween>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Grid.SpaceBetween verticalAlign="center">
|
<Grid.SpaceBetween verticalAlign="center">
|
||||||
<div>
|
<div>YesNo with error</div>
|
||||||
YesNo with error
|
<YesNo showError onCheck={action('yesno-2')} name="yesno-2" />
|
||||||
</div>
|
|
||||||
<YesNo
|
|
||||||
showError
|
|
||||||
onCheck={action('yesno-2')}
|
|
||||||
name="yesno-2"
|
|
||||||
/>
|
|
||||||
</Grid.SpaceBetween>
|
</Grid.SpaceBetween>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<Grid.SpaceBetween verticalAlign="center">
|
<Grid.SpaceBetween verticalAlign="center">
|
||||||
<div>
|
<div>YesNo disabled</div>
|
||||||
YesNo disabled
|
<YesNo disabled onCheck={action('yesno-3')} name="yesno-3" />
|
||||||
</div>
|
|
||||||
<YesNo
|
|
||||||
disabled
|
|
||||||
onCheck={action('yesno-3')}
|
|
||||||
name="yesno-3"
|
|
||||||
/>
|
|
||||||
</Grid.SpaceBetween>
|
</Grid.SpaceBetween>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
export default (
|
export default (
|
||||||
<svg width="17" height="16" viewBox="0 0 17 16">
|
<svg width="17" height="16" viewBox="0 0 17 16">
|
||||||
<path fill="currentColor" d="M12.407 3.31c.576.576.576 1.509 0 2.084L9.914 7.888l2.411 2.412c.586.585.586 1.535 0 2.121-.585.586-1.535.586-2.12 0l-2.413-2.412L5.3 12.503c-.576.575-1.509.575-2.084 0-.575-.575-.575-1.508 0-2.083l2.493-2.495-2.41-2.41c-.587-.587-.587-1.536 0-2.122.585-.586 1.535-.586 2.12 0L7.83 5.804l2.494-2.493c.575-.576 1.508-.576 2.083 0z" />
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M12.407 3.31c.576.576.576 1.509 0 2.084L9.914 7.888l2.411 2.412c.586.585.586 1.535 0 2.121-.585.586-1.535.586-2.12 0l-2.413-2.412L5.3 12.503c-.576.575-1.509.575-2.084 0-.575-.575-.575-1.508 0-2.083l2.493-2.495-2.41-2.41c-.587-.587-.587-1.536 0-2.122.585-.586 1.535-.586 2.12 0L7.83 5.804l2.494-2.493c.575-.576 1.508-.576 2.083 0z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
export default (
|
export default (
|
||||||
<svg width="13" height="11" viewBox="0 0 13 11">
|
<svg width="13" height="11" viewBox="0 0 13 11">
|
||||||
<path fill="currentColor" d="M9.967.638c.483-.698 1.405-.846 2.06-.33.654.515.793 1.499.31 2.197l-5.44 7.857c-.55.794-1.64.857-2.267.132l-3.4-3.928c-.552-.638-.515-1.632.083-2.22.598-.59 1.53-.55 2.082.088l2.19 2.532L9.968.638z" />
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M9.967.638c.483-.698 1.405-.846 2.06-.33.654.515.793 1.499.31 2.197l-5.44 7.857c-.55.794-1.64.857-2.267.132l-3.4-3.928c-.552-.638-.515-1.632.083-2.22.598-.59 1.53-.55 2.082.088l2.19 2.532L9.968.638z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
@ -20,13 +20,11 @@ function YesNo({
|
|||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames({
|
||||||
classnames({
|
|
||||||
'mailpoet-form-yesno': true,
|
'mailpoet-form-yesno': true,
|
||||||
'mailpoet-form-yesno-error': showError,
|
'mailpoet-form-yesno-error': showError,
|
||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
})
|
})}
|
||||||
}
|
|
||||||
data-automation-id={automationId}
|
data-automation-id={automationId}
|
||||||
>
|
>
|
||||||
<label>
|
<label>
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { curry } from 'lodash';
|
import { curry } from 'lodash';
|
||||||
|
|
||||||
const setLowercaseValue = curry((setter: (value: string) => void, value: string) => {
|
const setLowercaseValue = curry(
|
||||||
|
(setter: (value: string) => void, value: string) => {
|
||||||
setter(value.toLowerCase());
|
setter(value.toLowerCase());
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default setLowercaseValue;
|
export default setLowercaseValue;
|
||||||
|
@ -8,27 +8,22 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function Layouts(): ReactElement {
|
export function Layouts(): ReactElement {
|
||||||
const content = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi libero sapien, tristique sollicitudin lobortis id, viverra id libero.';
|
const content =
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi libero sapien, tristique sollicitudin lobortis id, viverra id libero.';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Heading level={3}>Column</Heading>
|
<Heading level={3}>Column</Heading>
|
||||||
<Grid.Column className="custom-class">
|
<Grid.Column className="custom-class">{content}</Grid.Column>
|
||||||
{content}
|
|
||||||
</Grid.Column>
|
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
<Grid.Column align="center">
|
<Grid.Column align="center">{content}</Grid.Column>
|
||||||
{content}
|
|
||||||
</Grid.Column>
|
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
<Heading level={3}>Column - small</Heading>
|
<Heading level={3}>Column - small</Heading>
|
||||||
<Grid.Column dimension="small">
|
<Grid.Column dimension="small">{content}</Grid.Column>
|
||||||
{content}
|
|
||||||
</Grid.Column>
|
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
@ -77,9 +72,7 @@ export function Layouts(): ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
Part
|
Part
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>Right Part</div>
|
||||||
Right Part
|
|
||||||
</div>
|
|
||||||
</Grid.SpaceBetween>
|
</Grid.SpaceBetween>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
|
|
||||||
@ -93,22 +86,16 @@ export function Layouts(): ReactElement {
|
|||||||
<br />
|
<br />
|
||||||
Part
|
Part
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>Right Part</div>
|
||||||
Right Part
|
|
||||||
</div>
|
|
||||||
</Grid.SpaceBetween>
|
</Grid.SpaceBetween>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
|
|
||||||
<Heading level={3}>Centered row</Heading>
|
<Heading level={3}>Centered row</Heading>
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
<Grid.CenteredRow className="custom-class">
|
<Grid.CenteredRow className="custom-class">
|
||||||
<div>
|
<div>Left</div>
|
||||||
Left
|
|
||||||
</div>
|
|
||||||
<Input type="text" />
|
<Input type="text" />
|
||||||
<div>
|
<div>Right</div>
|
||||||
Right
|
|
||||||
</div>
|
|
||||||
</Grid.CenteredRow>
|
</Grid.CenteredRow>
|
||||||
</Grid.Column>
|
</Grid.Column>
|
||||||
</>
|
</>
|
||||||
|
@ -16,16 +16,10 @@ export function Column({
|
|||||||
}: Props): ReactElement {
|
}: Props): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames(className, 'mailpoet-grid-column', {
|
||||||
classnames(
|
|
||||||
className,
|
|
||||||
'mailpoet-grid-column',
|
|
||||||
{
|
|
||||||
[`mailpoet-grid-column-${dimension}`]: dimension,
|
[`mailpoet-grid-column-${dimension}`]: dimension,
|
||||||
[`mailpoet-grid-column-${align}`]: align,
|
[`mailpoet-grid-column-${align}`]: align,
|
||||||
},
|
})}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,15 +14,10 @@ export function SpaceBetween({
|
|||||||
}: Props): ReactElement {
|
}: Props): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={classnames(className, 'mailpoet-grid-space-between', {
|
||||||
classnames(
|
[`mailpoet-grid-space-between-vertical-${verticalAlign}`]:
|
||||||
className,
|
verticalAlign,
|
||||||
'mailpoet-grid-space-between',
|
})}
|
||||||
{
|
|
||||||
[`mailpoet-grid-space-between-vertical-${verticalAlign}`]: verticalAlign,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,9 +7,16 @@ type Props = {
|
|||||||
automationId?: string;
|
automationId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ThreeColumns({ children, className, automationId }: Props): ReactElement {
|
export function ThreeColumns({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
automationId,
|
||||||
|
}: Props): ReactElement {
|
||||||
return (
|
return (
|
||||||
<div className={classnames(className, 'mailpoet-grid-three-columns')} data-automation-id={automationId}>
|
<div
|
||||||
|
className={classnames(className, 'mailpoet-grid-three-columns')}
|
||||||
|
data-automation-id={automationId}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -6,8 +6,8 @@ function KeyValueTable(props) {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{props.rows.map((row) => (
|
{props.rows.map((row) => (
|
||||||
<tr key={`row_${row.key}`}>
|
<tr key={`row_${row.key}`}>
|
||||||
<td className="row-title">{ row.key }</td>
|
<td className="row-title">{row.key}</td>
|
||||||
<td>{ row.value }</td>
|
<td>{row.value}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -17,14 +17,16 @@ function KeyValueTable(props) {
|
|||||||
|
|
||||||
KeyValueTable.propTypes = {
|
KeyValueTable.propTypes = {
|
||||||
max_width: PropTypes.string,
|
max_width: PropTypes.string,
|
||||||
rows: PropTypes.arrayOf(PropTypes.shape({
|
rows: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
key: PropTypes.string.isRequired,
|
key: PropTypes.string.isRequired,
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.number,
|
PropTypes.number,
|
||||||
PropTypes.element,
|
PropTypes.element,
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
})).isRequired,
|
}),
|
||||||
|
).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyValueTable.defaultProps = {
|
KeyValueTable.defaultProps = {
|
||||||
|
@ -14,7 +14,10 @@ MailPoet.I18n.add('openedStatTooltipAverage', 'under 10%');
|
|||||||
MailPoet.I18n.add('clickedStatTooltipExcellent', 'above 3%');
|
MailPoet.I18n.add('clickedStatTooltipExcellent', 'above 3%');
|
||||||
MailPoet.I18n.add('clickedStatTooltipGood', 'between 1 and 3%');
|
MailPoet.I18n.add('clickedStatTooltipGood', 'between 1 and 3%');
|
||||||
MailPoet.I18n.add('clickedStatTooltipAverage', 'under 1%');
|
MailPoet.I18n.add('clickedStatTooltipAverage', 'under 1%');
|
||||||
MailPoet.I18n.add('revenueStatsTooltipShort', 'Revenues by customer who clicked on this email in the last 2 weeks.');
|
MailPoet.I18n.add(
|
||||||
|
'revenueStatsTooltipShort',
|
||||||
|
'Revenues by customer who clicked on this email in the last 2 weeks.',
|
||||||
|
);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'Listing',
|
title: 'Listing',
|
||||||
@ -36,11 +39,26 @@ export function NewsletterStatsComponent() {
|
|||||||
|
|
||||||
<Heading level={3}>With badges and revenues</Heading>
|
<Heading level={3}>With badges and revenues</Heading>
|
||||||
|
|
||||||
<NewsletterStats opened={1} clicked={0.1} revenues="10€" newsletterId={4} />
|
<NewsletterStats
|
||||||
|
opened={1}
|
||||||
|
clicked={0.1}
|
||||||
|
revenues="10€"
|
||||||
|
newsletterId={4}
|
||||||
|
/>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<NewsletterStats opened={11} clicked={1.1} revenues="100€" newsletterId={5} />
|
<NewsletterStats
|
||||||
|
opened={11}
|
||||||
|
clicked={1.1}
|
||||||
|
revenues="100€"
|
||||||
|
newsletterId={5}
|
||||||
|
/>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<NewsletterStats opened={31} clicked={3.1} revenues="1000€" newsletterId={6} />
|
<NewsletterStats
|
||||||
|
opened={31}
|
||||||
|
clicked={3.1}
|
||||||
|
revenues="1000€"
|
||||||
|
newsletterId={6}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
@ -56,11 +74,29 @@ export function NewsletterStatsComponent() {
|
|||||||
|
|
||||||
<Heading level={3}>No badges, with revenues</Heading>
|
<Heading level={3}>No badges, with revenues</Heading>
|
||||||
|
|
||||||
<NewsletterStats hideBadges opened={1} clicked={0.1} revenues="10€" newsletterId={10} />
|
<NewsletterStats
|
||||||
|
hideBadges
|
||||||
|
opened={1}
|
||||||
|
clicked={0.1}
|
||||||
|
revenues="10€"
|
||||||
|
newsletterId={10}
|
||||||
|
/>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<NewsletterStats hideBadges opened={11} clicked={1.1} revenues="100€" newsletterId={11} />
|
<NewsletterStats
|
||||||
|
hideBadges
|
||||||
|
opened={11}
|
||||||
|
clicked={1.1}
|
||||||
|
revenues="100€"
|
||||||
|
newsletterId={11}
|
||||||
|
/>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
<NewsletterStats hideBadges opened={31} clicked={3.1} revenues="1000€" newsletterId={12} />
|
<NewsletterStats
|
||||||
|
hideBadges
|
||||||
|
opened={31}
|
||||||
|
clicked={3.1}
|
||||||
|
revenues="1000€"
|
||||||
|
newsletterId={12}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,12 @@ export function NewsletterStatuses() {
|
|||||||
<NewsletterStatus total={200} processed={0} />
|
<NewsletterStatus total={200} processed={0} />
|
||||||
<NewsletterStatus total={400} processed={150} />
|
<NewsletterStatus total={400} processed={150} />
|
||||||
<NewsletterStatus scheduledFor={inPast} total={300} processed={270} />
|
<NewsletterStatus scheduledFor={inPast} total={300} processed={270} />
|
||||||
<NewsletterStatus scheduledFor={inPast} total={300} processed={270} isPaused />
|
<NewsletterStatus
|
||||||
|
scheduledFor={inPast}
|
||||||
|
total={300}
|
||||||
|
processed={270}
|
||||||
|
isPaused
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
export { default as NewsletterStats } from './newsletter_stats';
|
export { default as NewsletterStats } from './newsletter_stats';
|
||||||
export { default as NewsletterStatus, ScheduledIcon } from './newsletter_status';
|
export {
|
||||||
|
default as NewsletterStatus,
|
||||||
|
ScheduledIcon,
|
||||||
|
} from './newsletter_status';
|
||||||
|
@ -32,8 +32,7 @@ function NewsletterStats({
|
|||||||
%
|
%
|
||||||
<br />
|
<br />
|
||||||
<span className="mailpoet-listing-stats-percentages-opens">
|
<span className="mailpoet-listing-stats-percentages-opens">
|
||||||
{openedDisplay}
|
{openedDisplay}%
|
||||||
%
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{!hideBadges && (
|
{!hideBadges && (
|
||||||
@ -53,12 +52,10 @@ function NewsletterStats({
|
|||||||
const revenuesTooltipId = `revenues-${newsletterId || '0'}`;
|
const revenuesTooltipId = `revenues-${newsletterId || '0'}`;
|
||||||
revenueStats = (
|
revenueStats = (
|
||||||
<div>
|
<div>
|
||||||
<Tag data-tip data-for={revenuesTooltipId}>{revenues}</Tag>
|
<Tag data-tip data-for={revenuesTooltipId}>
|
||||||
<Tooltip
|
{revenues}
|
||||||
place="top"
|
</Tag>
|
||||||
multiline
|
<Tooltip place="top" multiline id={revenuesTooltipId}>
|
||||||
id={revenuesTooltipId}
|
|
||||||
>
|
|
||||||
<div className="mailpoet-listing-stats-tooltip-content">
|
<div className="mailpoet-listing-stats-tooltip-content">
|
||||||
{MailPoet.I18n.t('revenueStatsTooltipShort')}
|
{MailPoet.I18n.t('revenueStatsTooltipShort')}
|
||||||
</div>
|
</div>
|
||||||
@ -68,7 +65,10 @@ function NewsletterStats({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (wrapContentInLink) {
|
if (wrapContentInLink) {
|
||||||
clickedAndOpenedStats = wrapContentInLink(clickedAndOpenedStats, 'opened-and-clicked');
|
clickedAndOpenedStats = wrapContentInLink(
|
||||||
|
clickedAndOpenedStats,
|
||||||
|
'opened-and-clicked',
|
||||||
|
);
|
||||||
revenueStats = wrapContentInLink(revenueStats, 'revenue');
|
revenueStats = wrapContentInLink(revenueStats, 'revenue');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,15 +22,10 @@ function Badge({
|
|||||||
}: BadgeProps) {
|
}: BadgeProps) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<Tag
|
<Tag isInverted={isInverted} variant={type} data-tip data-for={tooltipId}>
|
||||||
isInverted={isInverted}
|
|
||||||
variant={type}
|
|
||||||
data-tip
|
|
||||||
data-for={tooltipId}
|
|
||||||
>
|
|
||||||
{name}
|
{name}
|
||||||
</Tag>
|
</Tag>
|
||||||
{ tooltip && (
|
{tooltip && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
place={tooltipPlace || 'top'}
|
place={tooltipPlace || 'top'}
|
||||||
multiline
|
multiline
|
||||||
@ -38,7 +33,7 @@ function Badge({
|
|||||||
>
|
>
|
||||||
{tooltip}
|
{tooltip}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) }
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,7 @@ type StatsBadgeProps = {
|
|||||||
const stats = {
|
const stats = {
|
||||||
opened: {
|
opened: {
|
||||||
badgeRanges: [30, 10, 0],
|
badgeRanges: [30, 10, 0],
|
||||||
badgeTypes: [
|
badgeTypes: ['excellent', 'good', 'average'],
|
||||||
'excellent',
|
|
||||||
'good',
|
|
||||||
'average',
|
|
||||||
],
|
|
||||||
tooltipText: [
|
tooltipText: [
|
||||||
MailPoet.I18n.t('openedStatTooltipExcellent'),
|
MailPoet.I18n.t('openedStatTooltipExcellent'),
|
||||||
MailPoet.I18n.t('openedStatTooltipGood'),
|
MailPoet.I18n.t('openedStatTooltipGood'),
|
||||||
@ -26,11 +22,7 @@ const stats = {
|
|||||||
},
|
},
|
||||||
clicked: {
|
clicked: {
|
||||||
badgeRanges: [3, 1, 0],
|
badgeRanges: [3, 1, 0],
|
||||||
badgeTypes: [
|
badgeTypes: ['excellent', 'good', 'average'],
|
||||||
'excellent',
|
|
||||||
'good',
|
|
||||||
'average',
|
|
||||||
],
|
|
||||||
tooltipText: [
|
tooltipText: [
|
||||||
MailPoet.I18n.t('clickedStatTooltipExcellent'),
|
MailPoet.I18n.t('clickedStatTooltipExcellent'),
|
||||||
MailPoet.I18n.t('clickedStatTooltipGood'),
|
MailPoet.I18n.t('clickedStatTooltipGood'),
|
||||||
@ -92,24 +84,15 @@ export function StatsBadge(props: StatsBadgeProps) {
|
|||||||
{badge.tooltipTitle.toUpperCase()}
|
{badge.tooltipTitle.toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div className="mailpoet-listing-stats-tooltip-content">
|
<div className="mailpoet-listing-stats-tooltip-content">
|
||||||
<Badge
|
<Badge type="excellent" name={badges.excellent.name} />
|
||||||
type="excellent"
|
|
||||||
name={badges.excellent.name}
|
|
||||||
/>
|
|
||||||
{' : '}
|
{' : '}
|
||||||
{stat.tooltipText[0]}
|
{stat.tooltipText[0]}
|
||||||
<br />
|
<br />
|
||||||
<Badge
|
<Badge type="good" name={badges.good.name} />
|
||||||
type="good"
|
|
||||||
name={badges.good.name}
|
|
||||||
/>
|
|
||||||
{' : '}
|
{' : '}
|
||||||
{stat.tooltipText[1]}
|
{stat.tooltipText[1]}
|
||||||
<br />
|
<br />
|
||||||
<Badge
|
<Badge type="average" name={badges.average.name} />
|
||||||
type="average"
|
|
||||||
name={badges.average.name}
|
|
||||||
/>
|
|
||||||
{' : '}
|
{' : '}
|
||||||
{stat.tooltipText[2]}
|
{stat.tooltipText[2]}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import MailPoet from 'mailpoet';
|
import MailPoet from 'mailpoet';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import { addDays, differenceInMinutes, isFuture, isPast } from 'date-fns';
|
||||||
addDays, differenceInMinutes, isFuture, isPast,
|
|
||||||
} from 'date-fns';
|
|
||||||
import t from 'common/functions/t';
|
import t from 'common/functions/t';
|
||||||
import Tooltip from '../tooltip/tooltip';
|
import Tooltip from '../tooltip/tooltip';
|
||||||
|
|
||||||
@ -15,8 +13,18 @@ function CircularProgress({ percentage }: CircularProgressProps) {
|
|||||||
const filled = perimeter * (percentage / 100);
|
const filled = perimeter * (percentage / 100);
|
||||||
const empty = perimeter - filled;
|
const empty = perimeter - filled;
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg
|
||||||
<circle cx="12" cy="12" r="8" className="mailpoet-listing-status-percentage-background" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="8"
|
||||||
|
className="mailpoet-listing-status-percentage-background"
|
||||||
|
/>
|
||||||
<circle
|
<circle
|
||||||
r="8"
|
r="8"
|
||||||
cx="12"
|
cx="12"
|
||||||
@ -32,8 +40,17 @@ function CircularProgress({ percentage }: CircularProgressProps) {
|
|||||||
|
|
||||||
export function ScheduledIcon() {
|
export function ScheduledIcon() {
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg
|
||||||
<path className="mailpoet-listing-status-scheduled-icon" strokeLinecap="round" d="M12 7L12 12 15 15" />
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
className="mailpoet-listing-status-scheduled-icon"
|
||||||
|
strokeLinecap="round"
|
||||||
|
d="M12 7L12 12 15 15"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -55,18 +72,20 @@ function NewsletterStatus({
|
|||||||
}: NewsletterStatusProps) {
|
}: NewsletterStatusProps) {
|
||||||
const unknown = !scheduledFor && !processed && !total;
|
const unknown = !scheduledFor && !processed && !total;
|
||||||
const scheduled = scheduledFor && isFuture(scheduledFor);
|
const scheduled = scheduledFor && isFuture(scheduledFor);
|
||||||
const inProgress = (!scheduledFor || isPast(scheduledFor)) && processed < total;
|
const inProgress =
|
||||||
|
(!scheduledFor || isPast(scheduledFor)) && processed < total;
|
||||||
const sent = (!scheduledFor || isPast(scheduledFor)) && processed >= total;
|
const sent = (!scheduledFor || isPast(scheduledFor)) && processed >= total;
|
||||||
const sentWithoutQueue = status === 'sent' && total === undefined;
|
const sentWithoutQueue = status === 'sent' && total === undefined;
|
||||||
let percentage = 0;
|
let percentage = 0;
|
||||||
let label : string | JSX.Element = t('notSentYet');
|
let label: string | JSX.Element = t('notSentYet');
|
||||||
if (scheduled) {
|
if (scheduled) {
|
||||||
const scheduledDate = MailPoet.Date.short(scheduledFor);
|
const scheduledDate = MailPoet.Date.short(scheduledFor);
|
||||||
const scheduledTime = MailPoet.Date.time(scheduledFor);
|
const scheduledTime = MailPoet.Date.time(scheduledFor);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const tomorrow = addDays(now, 1);
|
const tomorrow = addDays(now, 1);
|
||||||
const isScheduledForToday = MailPoet.Date.short(now) === scheduledDate;
|
const isScheduledForToday = MailPoet.Date.short(now) === scheduledDate;
|
||||||
const isScheduledForTomorrow = MailPoet.Date.short(tomorrow) === scheduledDate;
|
const isScheduledForTomorrow =
|
||||||
|
MailPoet.Date.short(tomorrow) === scheduledDate;
|
||||||
if (isScheduledForToday || isScheduledForTomorrow) {
|
if (isScheduledForToday || isScheduledForTomorrow) {
|
||||||
const randomId = Math.random().toString(36).substring(2, 15);
|
const randomId = Math.random().toString(36).substring(2, 15);
|
||||||
const dateWord = isScheduledForToday ? t('today') : t('tomorrow');
|
const dateWord = isScheduledForToday ? t('today') : t('tomorrow');
|
||||||
@ -99,10 +118,14 @@ function NewsletterStatus({
|
|||||||
percentage = 100;
|
percentage = 100;
|
||||||
}
|
}
|
||||||
} else if (inProgress) {
|
} else if (inProgress) {
|
||||||
label = `${MailPoet.Num.toLocaleFixed(processed)} / ${MailPoet.Num.toLocaleFixed(total)}`;
|
label = `${MailPoet.Num.toLocaleFixed(
|
||||||
|
processed,
|
||||||
|
)} / ${MailPoet.Num.toLocaleFixed(total)}`;
|
||||||
percentage = 100 * (processed / total);
|
percentage = 100 * (processed / total);
|
||||||
} else if (sent) {
|
} else if (sent) {
|
||||||
label = `${MailPoet.Num.toLocaleFixed(total)} / ${MailPoet.Num.toLocaleFixed(total)}`;
|
label = `${MailPoet.Num.toLocaleFixed(
|
||||||
|
total,
|
||||||
|
)} / ${MailPoet.Num.toLocaleFixed(total)}`;
|
||||||
percentage = 100;
|
percentage = 100;
|
||||||
} else if (sentWithoutQueue) {
|
} else if (sentWithoutQueue) {
|
||||||
label = t('sent');
|
label = t('sent');
|
||||||
@ -112,7 +135,8 @@ function NewsletterStatus({
|
|||||||
label = t('paused');
|
label = t('paused');
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={classNames({
|
<div
|
||||||
|
className={classNames({
|
||||||
'mailpoet-listing-status': true,
|
'mailpoet-listing-status': true,
|
||||||
'mailpoet-listing-status-unknown': unknown,
|
'mailpoet-listing-status-unknown': unknown,
|
||||||
'mailpoet-listing-status-scheduled': scheduled,
|
'mailpoet-listing-status-scheduled': scheduled,
|
||||||
|
@ -9,24 +9,16 @@ export function Loaders() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>
|
<p>
|
||||||
Default loader:
|
Default loader: <Loader />
|
||||||
{' '}
|
|
||||||
<Loader />
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Light loader:
|
Light loader: <Loader variant="light" />
|
||||||
{' '}
|
|
||||||
<Loader variant="light" />
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Dark loader:
|
Dark loader: <Loader variant="dark" />
|
||||||
{' '}
|
|
||||||
<Loader variant="dark" />
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
bigger loader:
|
bigger loader: <Loader size={64} />
|
||||||
{' '}
|
|
||||||
<Loader size={64} />
|
|
||||||
</p>
|
</p>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -6,21 +6,50 @@ import Heading from '../../typography/heading/heading';
|
|||||||
const shortContent = (
|
const shortContent = (
|
||||||
<>
|
<>
|
||||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
|
||||||
<p>Morbi libero sapien, tristique sollicitudin lobortis id, viverra id libero.</p>
|
<p>
|
||||||
|
Morbi libero sapien, tristique sollicitudin lobortis id, viverra id
|
||||||
|
libero.
|
||||||
|
</p>
|
||||||
<p>Mauris dolor felis, sagittis at, luctus sed, aliquam non, tellus.</p>
|
<p>Mauris dolor felis, sagittis at, luctus sed, aliquam non, tellus.</p>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
const longContent = (
|
const longContent = (
|
||||||
<>
|
<>
|
||||||
<p>{'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(20)}</p>
|
<p>
|
||||||
<p>{'Morbi libero sapien, tristique sollicitudin lobortis id, viverra id libero. '.repeat(20)}</p>
|
{'Lorem ipsum dolor sit amet, consectetur adipiscing elit. '.repeat(20)}
|
||||||
<p>{'Mauris dolor felis, sagittis at, luctus sed, aliquam non, tellus. '.repeat(20)}</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
{'Morbi libero sapien, tristique sollicitudin lobortis id, viverra id libero. '.repeat(
|
||||||
|
20,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{'Mauris dolor felis, sagittis at, luctus sed, aliquam non, tellus. '.repeat(
|
||||||
|
20,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
<p>{'Vivamus ac leo pretium faucibus.'.repeat(20)}</p>
|
<p>{'Vivamus ac leo pretium faucibus.'.repeat(20)}</p>
|
||||||
<p>{'Etiam dui sem, fermentum vitae, sagittis id, malesuada in, quam. '.repeat(20)}</p>
|
<p>
|
||||||
<p>{'Duis sapien nunc, commodo et, interdum suscipit, sollicitudin et, dolor. '.repeat(20)}</p>
|
{'Etiam dui sem, fermentum vitae, sagittis id, malesuada in, quam. '.repeat(
|
||||||
<p>{'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. '.repeat(20)}</p>
|
20,
|
||||||
<p>{'Cras pede libero, dapibus nec, pretium sit amet, tempor quis. '.repeat(20)}</p>
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{'Duis sapien nunc, commodo et, interdum suscipit, sollicitudin et, dolor. '.repeat(
|
||||||
|
20,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. '.repeat(
|
||||||
|
20,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{'Cras pede libero, dapibus nec, pretium sit amet, tempor quis. '.repeat(
|
||||||
|
20,
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -55,7 +84,7 @@ function ModalWrapper({
|
|||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</Modal>
|
</Modal>
|
||||||
) }
|
)}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -65,25 +94,71 @@ export function Modals() {
|
|||||||
<>
|
<>
|
||||||
<Heading level={3}>Modal with short text</Heading>
|
<Heading level={3}>Modal with short text</Heading>
|
||||||
<ModalWrapper buttonCaption="Show modal with title, with close button" />
|
<ModalWrapper buttonCaption="Show modal with title, with close button" />
|
||||||
<ModalWrapper buttonCaption="Show modal with title, without close button" isDismissible={false} />
|
<ModalWrapper
|
||||||
<ModalWrapper buttonCaption="Show modal without title, with close button" title={null} />
|
buttonCaption="Show modal with title, without close button"
|
||||||
<ModalWrapper buttonCaption="Show modal without title, without close button" title={null} isDismissible={false} />
|
isDismissible={false}
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal without title, with close button"
|
||||||
|
title={null}
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal without title, without close button"
|
||||||
|
title={null}
|
||||||
|
isDismissible={false}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
<Heading level={3}>Modal with long text</Heading>
|
<Heading level={3}>Modal with long text</Heading>
|
||||||
<ModalWrapper buttonCaption="Show modal with title, with close button" content={longContent} />
|
<ModalWrapper
|
||||||
<ModalWrapper buttonCaption="Show modal with title, without close button" isDismissible={false} content={longContent} />
|
buttonCaption="Show modal with title, with close button"
|
||||||
<ModalWrapper buttonCaption="Show modal without title, with close button" title={null} content={longContent} />
|
content={longContent}
|
||||||
<ModalWrapper buttonCaption="Show modal without title, without close button" title={null} isDismissible={false} content={longContent} />
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal with title, without close button"
|
||||||
|
isDismissible={false}
|
||||||
|
content={longContent}
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal without title, with close button"
|
||||||
|
title={null}
|
||||||
|
content={longContent}
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal without title, without close button"
|
||||||
|
title={null}
|
||||||
|
isDismissible={false}
|
||||||
|
content={longContent}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
|
|
||||||
<Heading level={3}>Full-screen modal</Heading>
|
<Heading level={3}>Full-screen modal</Heading>
|
||||||
<ModalWrapper buttonCaption="Show modal with title, with close button" content={longContent} fullScreen />
|
<ModalWrapper
|
||||||
<ModalWrapper buttonCaption="Show modal with title, without close button" isDismissible={false} content={longContent} fullScreen />
|
buttonCaption="Show modal with title, with close button"
|
||||||
<ModalWrapper buttonCaption="Show modal without title, with close button" title={null} content={longContent} fullScreen />
|
content={longContent}
|
||||||
<ModalWrapper buttonCaption="Show modal without title, without close button" title={null} isDismissible={false} content={longContent} fullScreen />
|
fullScreen
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal with title, without close button"
|
||||||
|
isDismissible={false}
|
||||||
|
content={longContent}
|
||||||
|
fullScreen
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal without title, with close button"
|
||||||
|
title={null}
|
||||||
|
content={longContent}
|
||||||
|
fullScreen
|
||||||
|
/>
|
||||||
|
<ModalWrapper
|
||||||
|
buttonCaption="Show modal without title, without close button"
|
||||||
|
title={null}
|
||||||
|
isDismissible={false}
|
||||||
|
content={longContent}
|
||||||
|
fullScreen
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,7 @@ type Props = {
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ModalFrame({
|
function ModalFrame({ fullScreen = false, className = '', children }: Props) {
|
||||||
fullScreen = false,
|
|
||||||
className = '',
|
|
||||||
children,
|
|
||||||
}: Props) {
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames(
|
className={classnames(
|
||||||
|
@ -7,9 +7,7 @@ type Props = {
|
|||||||
function ModalHeader({ title }: Props) {
|
function ModalHeader({ title }: Props) {
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-modal-header">
|
<div className="mailpoet-modal-header">
|
||||||
<Heading level={3}>
|
<Heading level={3}>{title}</Heading>
|
||||||
{ title }
|
|
||||||
</Heading>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -38,14 +38,9 @@ function Modal({
|
|||||||
shouldCloseOnClickOutside={shouldCloseOnClickOutside}
|
shouldCloseOnClickOutside={shouldCloseOnClickOutside}
|
||||||
className={overlayClassName}
|
className={overlayClassName}
|
||||||
>
|
>
|
||||||
<ModalFrame
|
<ModalFrame className={contentClassName} fullScreen={fullScreen}>
|
||||||
className={contentClassName}
|
{title && <ModalHeader title={title} />}
|
||||||
fullScreen={fullScreen}
|
{isDismissible && (
|
||||||
>
|
|
||||||
{ title && (
|
|
||||||
<ModalHeader title={title} />
|
|
||||||
) }
|
|
||||||
{ isDismissible && (
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onRequestClose}
|
onClick={onRequestClose}
|
||||||
@ -54,12 +49,9 @@ function Modal({
|
|||||||
>
|
>
|
||||||
{ModalCloseIcon}
|
{ModalCloseIcon}
|
||||||
</button>
|
</button>
|
||||||
) }
|
)}
|
||||||
<div
|
<div className="mailpoet-modal-content" role="document">
|
||||||
className="mailpoet-modal-content"
|
{children}
|
||||||
role="document"
|
|
||||||
>
|
|
||||||
{ children }
|
|
||||||
</div>
|
</div>
|
||||||
</ModalFrame>
|
</ModalFrame>
|
||||||
</ModalOverlay>,
|
</ModalOverlay>,
|
||||||
|
@ -31,25 +31,27 @@ const getBannerMessage = (translationKey: string) => {
|
|||||||
{ReactStringReplace(
|
{ReactStringReplace(
|
||||||
message,
|
message,
|
||||||
/(\[subscribersCount]|\[subscribersLimit])/g,
|
/(\[subscribersCount]|\[subscribersLimit])/g,
|
||||||
(match) => ((match === '[subscribersCount]') ? subscribersCount : subscribersLimit),
|
(match) =>
|
||||||
|
match === '[subscribersCount]' ? subscribersCount : subscribersLimit,
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCtaButton = (translationKey: string, link: string, target = '_blank') => (
|
const getCtaButton = (
|
||||||
<Button
|
translationKey: string,
|
||||||
href={link}
|
link: string,
|
||||||
target={target}
|
target = '_blank',
|
||||||
rel="noopener noreferrer"
|
) => (
|
||||||
>
|
<Button href={link} target={target} rel="noopener noreferrer">
|
||||||
{MailPoet.I18n.t(translationKey)}
|
{MailPoet.I18n.t(translationKey)}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
function PremiumBannerWithUpgrade(
|
function PremiumBannerWithUpgrade({
|
||||||
{ message, actionButton }: Props,
|
message,
|
||||||
) : JSX.Element {
|
actionButton,
|
||||||
|
}: Props): JSX.Element {
|
||||||
let bannerMessage: ReactNode;
|
let bannerMessage: ReactNode;
|
||||||
let ctaButton: ReactNode;
|
let ctaButton: ReactNode;
|
||||||
|
|
||||||
@ -57,14 +59,25 @@ function PremiumBannerWithUpgrade(
|
|||||||
bannerMessage = getBannerMessage('premiumFeatureDescription');
|
bannerMessage = getBannerMessage('premiumFeatureDescription');
|
||||||
|
|
||||||
ctaButton = isPremiumPluginInstalled
|
ctaButton = isPremiumPluginInstalled
|
||||||
? getCtaButton('premiumFeatureButtonActivatePremium', premiumPluginActivationUrl, '_self')
|
? getCtaButton(
|
||||||
: getCtaButton('premiumFeatureButtonDownloadPremium', premiumPluginDownloadUrl);
|
'premiumFeatureButtonActivatePremium',
|
||||||
|
premiumPluginActivationUrl,
|
||||||
|
'_self',
|
||||||
|
)
|
||||||
|
: getCtaButton(
|
||||||
|
'premiumFeatureButtonDownloadPremium',
|
||||||
|
premiumPluginDownloadUrl,
|
||||||
|
);
|
||||||
} else if (subscribersLimitReached) {
|
} else if (subscribersLimitReached) {
|
||||||
bannerMessage = getBannerMessage('premiumFeatureDescriptionSubscribersLimitReached');
|
bannerMessage = getBannerMessage(
|
||||||
|
'premiumFeatureDescriptionSubscribersLimitReached',
|
||||||
|
);
|
||||||
|
|
||||||
const link = anyValidKey
|
const link = anyValidKey
|
||||||
? MailPoet.MailPoetComUrlFactory.getUpgradeUrl(pluginPartialKey)
|
? MailPoet.MailPoetComUrlFactory.getUpgradeUrl(pluginPartialKey)
|
||||||
: MailPoet.MailPoetComUrlFactory.getPurchasePlanUrl(+subscribersCount + 1);
|
: MailPoet.MailPoetComUrlFactory.getPurchasePlanUrl(
|
||||||
|
+subscribersCount + 1,
|
||||||
|
);
|
||||||
|
|
||||||
ctaButton = getCtaButton('premiumFeatureButtonUpgradePlan', link);
|
ctaButton = getCtaButton('premiumFeatureButtonUpgradePlan', link);
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,18 +11,16 @@ export function PremiumsRequired() {
|
|||||||
<div>
|
<div>
|
||||||
<PremiumRequired
|
<PremiumRequired
|
||||||
title="This is a Premium Feature"
|
title="This is a Premium Feature"
|
||||||
message={(
|
message={
|
||||||
<p>
|
<p>
|
||||||
Learn more about your subscribers and optimize your campaigns. See who
|
Learn more about your subscribers and optimize your campaigns. See
|
||||||
opened your emails, which links they clicked, and then use the data to make y
|
who opened your emails, which links they clicked, and then use the
|
||||||
our emails even better. And if you run a WooCommerce store, you will also
|
data to make y our emails even better. And if you run a WooCommerce
|
||||||
see the revenue earned per email.
|
store, you will also see the revenue earned per email.
|
||||||
<a href="#">
|
<a href="#">Learn more.</a>
|
||||||
Learn more.
|
|
||||||
</a>
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
}
|
||||||
actionButton={(<Button href="#">Sign Up</Button>)}
|
actionButton={<Button href="#">Sign Up</Button>}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,15 +13,11 @@ function PremiumRequired({ title, message, actionButton }: Props) {
|
|||||||
<div className="mailpoet-premium-required">
|
<div className="mailpoet-premium-required">
|
||||||
<div className="mailpoet-premium-required-message">
|
<div className="mailpoet-premium-required-message">
|
||||||
<Heading level={5}>
|
<Heading level={5}>
|
||||||
<Badge title="Premium" />
|
<Badge title="Premium" /> {title}
|
||||||
{' '}
|
|
||||||
{title}
|
|
||||||
</Heading>
|
</Heading>
|
||||||
{message}
|
{message}
|
||||||
</div>
|
</div>
|
||||||
<div className="mailpoet-premium-required-button">
|
<div className="mailpoet-premium-required-button">{actionButton}</div>
|
||||||
{actionButton}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,19 @@
|
|||||||
export default function DesktopIcon() {
|
export default function DesktopIcon() {
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="24px"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
version="1.1"
|
||||||
|
>
|
||||||
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
||||||
<g className="mailpoet_preview_icon_fill" transform="translate(-712.000000, -64.000000)" fillRule="nonzero">
|
<g
|
||||||
|
className="mailpoet_preview_icon_fill"
|
||||||
|
transform="translate(-712.000000, -64.000000)"
|
||||||
|
fillRule="nonzero"
|
||||||
|
>
|
||||||
<g transform="translate(712.000000, 64.000000)">
|
<g transform="translate(712.000000, 64.000000)">
|
||||||
<g>
|
<g>
|
||||||
<path d="M13.965,20.5 C14.1138804,20.5 14.2550143,20.5663566 14.35,20.681 L14.35,20.681 L16.449,23.213 C16.5561084,23.3657981 16.5692196,23.5655248 16.4830056,23.7310137 C16.3967915,23.8965027 16.2255996,24.0002137 16.039,24 L16.039,24 L7.96,24 C7.7734004,24.0002137 7.60220849,23.8965027 7.51599443,23.7310137 C7.42978036,23.5655248 7.44289164,23.3657981 7.55,23.213 L7.55,23.213 L9.65,20.681 C9.74498572,20.5663566 9.88611956,20.5 10.035,20.5 L10.035,20.5 Z M22.5,-4.08562073e-14 C23.3284271,-4.08562073e-14 24,0.671572875 24,1.5 L24,1.5 L24,17.5 C24,18.3284271 23.3284271,19 22.5,19 L22.5,19 L1.5,19 C0.671572875,19 0,18.3284271 0,17.5 L0,17.5 L0,1.5 C0,0.671572875 0.671572875,-4.08562073e-14 1.5,-4.08562073e-14 L1.5,-4.08562073e-14 Z M21.5,2 L2.5,2 C2.22385763,2 2,2.22385763 2,2.5 L2,2.5 L2,14.5 C2,14.7761424 2.22385763,15 2.5,15 L2.5,15 L21.5,15 C21.7761424,15 22,14.7761424 22,14.5 L22,14.5 L22,2.5 C22,2.22385763 21.7761424,2 21.5,2 L21.5,2 Z" />
|
<path d="M13.965,20.5 C14.1138804,20.5 14.2550143,20.5663566 14.35,20.681 L14.35,20.681 L16.449,23.213 C16.5561084,23.3657981 16.5692196,23.5655248 16.4830056,23.7310137 C16.3967915,23.8965027 16.2255996,24.0002137 16.039,24 L16.039,24 L7.96,24 C7.7734004,24.0002137 7.60220849,23.8965027 7.51599443,23.7310137 C7.42978036,23.5655248 7.44289164,23.3657981 7.55,23.213 L7.55,23.213 L9.65,20.681 C9.74498572,20.5663566 9.88611956,20.5 10.035,20.5 L10.035,20.5 Z M22.5,-4.08562073e-14 C23.3284271,-4.08562073e-14 24,0.671572875 24,1.5 L24,1.5 L24,17.5 C24,18.3284271 23.3284271,19 22.5,19 L22.5,19 L1.5,19 C0.671572875,19 0,18.3284271 0,17.5 L0,17.5 L0,1.5 C0,0.671572875 0.671572875,-4.08562073e-14 1.5,-4.08562073e-14 L1.5,-4.08562073e-14 Z M21.5,2 L2.5,2 C2.22385763,2 2,2.22385763 2,2.5 L2,2.5 L2,14.5 C2,14.7761424 2.22385763,15 2.5,15 L2.5,15 L21.5,15 C21.7761424,15 22,14.7761424 22,14.5 L22,14.5 L22,2.5 C22,2.22385763 21.7761424,2 21.5,2 L21.5,2 Z" />
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
export default function MobileIcon() {
|
export default function MobileIcon() {
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="16px" height="24px" viewBox="0 0 16 24" version="1.1">
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="16px"
|
||||||
|
height="24px"
|
||||||
|
viewBox="0 0 16 24"
|
||||||
|
version="1.1"
|
||||||
|
>
|
||||||
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
||||||
<g className="mailpoet_preview_icon_fill" transform="translate(-760.000000, -64.000000)" fillRule="nonzero">
|
<g
|
||||||
|
className="mailpoet_preview_icon_fill"
|
||||||
|
transform="translate(-760.000000, -64.000000)"
|
||||||
|
fillRule="nonzero"
|
||||||
|
>
|
||||||
<g transform="translate(712.000000, 64.000000)">
|
<g transform="translate(712.000000, 64.000000)">
|
||||||
<g transform="translate(48.000000, 0.000000)">
|
<g transform="translate(48.000000, 0.000000)">
|
||||||
<path d="M16,3 C16,1.34314575 14.6568542,0 13,0 L3,0 C1.34314575,0 0,1.34314575 0,3 L0,21 C0,22.6568542 1.34314575,24 3,24 L13,24 C14.6568542,24 16,22.6568542 16,21 L16,3 Z M14,5.5 L14,17.5 C14,18.0522847 13.5522847,18.5 13,18.5 L3,18.5 C2.44771525,18.5 2,18.0522847 2,17.5 L2,5.5 C2,4.94771525 2.44771525,4.5 3,4.5 L13,4.5 C13.5522847,4.5 14,4.94771525 14,5.5 Z M7,21 C7,20.4477153 7.44771525,20 8,20 C8.55228475,20 9,20.4477153 9,21 C9,21.5522847 8.55228475,22 8,22 C7.44771525,22 7,21.5522847 7,21 Z" id="Shape" />
|
<path
|
||||||
|
d="M16,3 C16,1.34314575 14.6568542,0 13,0 L3,0 C1.34314575,0 0,1.34314575 0,3 L0,21 C0,22.6568542 1.34314575,24 3,24 L13,24 C14.6568542,24 16,22.6568542 16,21 L16,3 Z M14,5.5 L14,17.5 C14,18.0522847 13.5522847,18.5 13,18.5 L3,18.5 C2.44771525,18.5 2,18.0522847 2,17.5 L2,5.5 C2,4.94771525 2.44771525,4.5 3,4.5 L13,4.5 C13.5522847,4.5 14,4.94771525 14,5.5 Z M7,21 C7,20.4477153 7.44771525,20 8,20 C8.55228475,20 9,20.4477153 9,21 C9,21.5522847 8.55228475,22 8,22 C7.44771525,22 7,21.5522847 7,21 Z"
|
||||||
|
id="Shape"
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
@ -5,11 +5,7 @@ import classnames from 'classnames';
|
|||||||
import MobileIcon from './mobile_icon';
|
import MobileIcon from './mobile_icon';
|
||||||
import DesktopIcon from './desktop_icon';
|
import DesktopIcon from './desktop_icon';
|
||||||
|
|
||||||
function Preview({
|
function Preview({ children, onDisplayTypeChange, selectedDisplayType }) {
|
||||||
children,
|
|
||||||
onDisplayTypeChange,
|
|
||||||
selectedDisplayType,
|
|
||||||
}) {
|
|
||||||
const [displayType, setDisplayType] = useState(selectedDisplayType);
|
const [displayType, setDisplayType] = useState(selectedDisplayType);
|
||||||
const changeType = (type) => {
|
const changeType = (type) => {
|
||||||
setDisplayType(type);
|
setDisplayType(type);
|
||||||
@ -19,7 +15,9 @@ function Preview({
|
|||||||
<div className="mailpoet_browser_preview">
|
<div className="mailpoet_browser_preview">
|
||||||
<div className="mailpoet_browser_preview_toggle">
|
<div className="mailpoet_browser_preview_toggle">
|
||||||
<a
|
<a
|
||||||
className={classnames('mailpoet_browser_preview_icon', { mailpoet_active: displayType === 'desktop' })}
|
className={classnames('mailpoet_browser_preview_icon', {
|
||||||
|
mailpoet_active: displayType === 'desktop',
|
||||||
|
})}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
changeType('desktop');
|
changeType('desktop');
|
||||||
@ -31,7 +29,9 @@ function Preview({
|
|||||||
<DesktopIcon />
|
<DesktopIcon />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
className={classnames('mailpoet_browser_preview_icon', { mailpoet_active: displayType === 'mobile' })}
|
className={classnames('mailpoet_browser_preview_icon', {
|
||||||
|
mailpoet_active: displayType === 'mobile',
|
||||||
|
})}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
changeType('mobile');
|
changeType('mobile');
|
||||||
@ -46,16 +46,22 @@ function Preview({
|
|||||||
<div
|
<div
|
||||||
className={classnames(
|
className={classnames(
|
||||||
'mailpoet_browser_preview_container',
|
'mailpoet_browser_preview_container',
|
||||||
{ mailpoet_browser_preview_container_mobile: displayType !== 'desktop' },
|
{
|
||||||
{ mailpoet_browser_preview_container_desktop: displayType === 'desktop' },
|
mailpoet_browser_preview_container_mobile:
|
||||||
|
displayType !== 'desktop',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mailpoet_browser_preview_container_desktop:
|
||||||
|
displayType === 'desktop',
|
||||||
|
},
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="mailpoet_browser_preview_border">
|
<div className="mailpoet_browser_preview_border">{children}</div>
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{displayType !== 'desktop' && (
|
||||||
{(displayType !== 'desktop') && (
|
<p className="mailpoet_form_preview_disclaimer">
|
||||||
<p className="mailpoet_form_preview_disclaimer">{MailPoet.I18n.t('formPreviewMobileDisclaimer')}</p>
|
{MailPoet.I18n.t('formPreviewMobileDisclaimer')}
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -4,9 +4,9 @@ import MailPoet from 'mailpoet';
|
|||||||
function PrintBoolean(props) {
|
function PrintBoolean(props) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{(props.children === true && props.truthy)
|
{(props.children === true && props.truthy) ||
|
||||||
|| (props.children === false && props.falsy)
|
(props.children === false && props.falsy) ||
|
||||||
|| (props.unknown)}
|
props.unknown}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,10 @@ function SenderEmailAddressWarning({ emailAddress, mssActive }) {
|
|||||||
/(%1\$s|%2\$s|<em>.*<\/em>)/,
|
/(%1\$s|%2\$s|<em>.*<\/em>)/,
|
||||||
(match) => {
|
(match) => {
|
||||||
if (match === '%1$s') return suggestedEmailAddress;
|
if (match === '%1$s') return suggestedEmailAddress;
|
||||||
if (match === '%2$s') return <em key="sender-email">{ emailAddress }</em>;
|
if (match === '%2$s')
|
||||||
|
return <em key="sender-email">{emailAddress}</em>;
|
||||||
return <em key="reply-to">{match.replace(/<\/?em>/g, '')}</em>;
|
return <em key="reply-to">{match.replace(/<\/?em>/g, '')}</em>;
|
||||||
}
|
},
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="sender_email_address_warning">
|
<p className="sender_email_address_warning">
|
||||||
|
@ -11,16 +11,20 @@ import { ErrorResponse, isErrorResponse } from '../ajax';
|
|||||||
* @param {string|null} address
|
* @param {string|null} address
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
*/
|
*/
|
||||||
const handleSave = (address: string | null) => MailPoet.Ajax.post({
|
const handleSave = (address: string | null) =>
|
||||||
|
MailPoet.Ajax.post({
|
||||||
api_version: MailPoet.apiVersion,
|
api_version: MailPoet.apiVersion,
|
||||||
endpoint: 'settings',
|
endpoint: 'settings',
|
||||||
action: 'setAuthorizedFromAddress',
|
action: 'setAuthorizedFromAddress',
|
||||||
data: {
|
data: {
|
||||||
address,
|
address,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getErrorMessage = (error: ErrorResponse['errors'][number] | null, address: string | null): string => {
|
const getErrorMessage = (
|
||||||
|
error: ErrorResponse['errors'][number] | null,
|
||||||
|
address: string | null,
|
||||||
|
): string => {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return MailPoet.I18n.t('setFromAddressEmailUnknownError');
|
return MailPoet.I18n.t('setFromAddressEmailUnknownError');
|
||||||
}
|
}
|
||||||
@ -55,15 +59,21 @@ const getSuccessMessage = (): JSX.Element => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const removeUnauthorizedEmailNotices = () => {
|
const removeUnauthorizedEmailNotices = () => {
|
||||||
const unauthorizedEmailNotice = document.querySelector('[data-notice="unauthorized-email-addresses-notice"]');
|
const unauthorizedEmailNotice = document.querySelector(
|
||||||
|
'[data-notice="unauthorized-email-addresses-notice"]',
|
||||||
|
);
|
||||||
if (unauthorizedEmailNotice) {
|
if (unauthorizedEmailNotice) {
|
||||||
unauthorizedEmailNotice.remove();
|
unauthorizedEmailNotice.remove();
|
||||||
}
|
}
|
||||||
const unauthorizedEmailInNewsletterNotice = document.querySelector('[data-notice="unauthorized-email-in-newsletters-addresses-notice"]');
|
const unauthorizedEmailInNewsletterNotice = document.querySelector(
|
||||||
|
'[data-notice="unauthorized-email-in-newsletters-addresses-notice"]',
|
||||||
|
);
|
||||||
if (unauthorizedEmailInNewsletterNotice) {
|
if (unauthorizedEmailInNewsletterNotice) {
|
||||||
unauthorizedEmailInNewsletterNotice.remove();
|
unauthorizedEmailInNewsletterNotice.remove();
|
||||||
}
|
}
|
||||||
const unauthorizedEmailInNewsletterDynamicNotice = document.querySelector('[data-id="mailpoet_authorization_error"]');
|
const unauthorizedEmailInNewsletterDynamicNotice = document.querySelector(
|
||||||
|
'[data-id="mailpoet_authorization_error"]',
|
||||||
|
);
|
||||||
if (unauthorizedEmailInNewsletterDynamicNotice) {
|
if (unauthorizedEmailInNewsletterDynamicNotice) {
|
||||||
unauthorizedEmailInNewsletterDynamicNotice.remove();
|
unauthorizedEmailInNewsletterDynamicNotice.remove();
|
||||||
}
|
}
|
||||||
@ -86,8 +96,7 @@ function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
|
|||||||
contentClassName="set-from-address-modal"
|
contentClassName="set-from-address-modal"
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
{
|
{ReactStringReplace(
|
||||||
ReactStringReplace(
|
|
||||||
MailPoet.I18n.t('setFromAddressModalDescription'),
|
MailPoet.I18n.t('setFromAddressModalDescription'),
|
||||||
/\[link\](.*?)\[\/link\]/g,
|
/\[link\](.*?)\[\/link\]/g,
|
||||||
(match) => (
|
(match) => (
|
||||||
@ -100,8 +109,7 @@ function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
|
|||||||
{match}
|
{match}
|
||||||
</a>
|
</a>
|
||||||
),
|
),
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
@ -112,7 +120,9 @@ function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
|
|||||||
data-parsley-type="email"
|
data-parsley-type="email"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
setAddress(event.target.value.trim() || null);
|
setAddress(event.target.value.trim() || null);
|
||||||
const addressValidator = jQuery('#mailpoet-set-from-address-modal-input').parsley();
|
const addressValidator = jQuery(
|
||||||
|
'#mailpoet-set-from-address-modal-input',
|
||||||
|
).parsley();
|
||||||
addressValidator.removeError('saveError');
|
addressValidator.removeError('saveError');
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -122,7 +132,9 @@ function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
|
|||||||
type="submit"
|
type="submit"
|
||||||
value={MailPoet.I18n.t('setFromAddressModalSave')}
|
value={MailPoet.I18n.t('setFromAddressModalSave')}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
const addressValidator = jQuery('#mailpoet-set-from-address-modal-input').parsley();
|
const addressValidator = jQuery(
|
||||||
|
'#mailpoet-set-from-address-modal-input',
|
||||||
|
).parsley();
|
||||||
addressValidator.validate();
|
addressValidator.validate();
|
||||||
if (!addressValidator.isValid()) {
|
if (!addressValidator.isValid()) {
|
||||||
return;
|
return;
|
||||||
@ -137,9 +149,12 @@ function SetFromAddressModal({ onRequestClose, setAuthorizedAddress }: Props) {
|
|||||||
removeUnauthorizedEmailNotices();
|
removeUnauthorizedEmailNotices();
|
||||||
notices.success(getSuccessMessage(), { timeout: false });
|
notices.success(getSuccessMessage(), { timeout: false });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = isErrorResponse(e) && e.errors[0] ? e.errors[0] : null;
|
const error =
|
||||||
|
isErrorResponse(e) && e.errors[0] ? e.errors[0] : null;
|
||||||
if (error.error === 'unauthorized') {
|
if (error.error === 'unauthorized') {
|
||||||
MailPoet.trackEvent('Unauthorized email used', { 'Unauthorized email source': 'modal' });
|
MailPoet.trackEvent('Unauthorized email used', {
|
||||||
|
'Unauthorized email source': 'modal',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const message = getErrorMessage(error, address);
|
const message = getErrorMessage(error, address);
|
||||||
addressValidator.addError('saveError', { message });
|
addressValidator.addError('saveError', { message });
|
||||||
|
@ -22,8 +22,9 @@ export function StepsWithoutTitles() {
|
|||||||
<Heading level={3}>{`Step ${step}`}</Heading>
|
<Heading level={3}>{`Step ${step}`}</Heading>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta natus
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta natus
|
||||||
consequuntur saepe harum nesciunt eum, a nulla facilis architecto incidunt
|
consequuntur saepe harum nesciunt eum, a nulla facilis architecto
|
||||||
odio voluptas praesentium, ipsa laboriosam animi! Officiis atque odio nulla.
|
incidunt odio voluptas praesentium, ipsa laboriosam animi! Officiis
|
||||||
|
atque odio nulla.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
@ -34,11 +35,7 @@ export function StepsWithoutTitles() {
|
|||||||
>
|
>
|
||||||
Previous step
|
Previous step
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={nextStep} dimension="small" isDisabled={step === 5}>
|
||||||
onClick={nextStep}
|
|
||||||
dimension="small"
|
|
||||||
isDisabled={step === 5}
|
|
||||||
>
|
|
||||||
Next step
|
Next step
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -55,13 +52,18 @@ export function StepsWithTitles() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Steps count={5} current={step} titles={['First', 'Second', 'Third', 'Fourth', 'Fifth']} />
|
<Steps
|
||||||
|
count={5}
|
||||||
|
current={step}
|
||||||
|
titles={['First', 'Second', 'Third', 'Fourth', 'Fifth']}
|
||||||
|
/>
|
||||||
<StepsContent>
|
<StepsContent>
|
||||||
<Heading level={3}>{`Step ${step}`}</Heading>
|
<Heading level={3}>{`Step ${step}`}</Heading>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta natus
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Soluta natus
|
||||||
consequuntur saepe harum nesciunt eum, a nulla facilis architecto incidunt
|
consequuntur saepe harum nesciunt eum, a nulla facilis architecto
|
||||||
odio voluptas praesentium, ipsa laboriosam animi! Officiis atque odio nulla.
|
incidunt odio voluptas praesentium, ipsa laboriosam animi! Officiis
|
||||||
|
atque odio nulla.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
@ -72,11 +74,7 @@ export function StepsWithTitles() {
|
|||||||
>
|
>
|
||||||
Previous step
|
Previous step
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button onClick={nextStep} dimension="small" isDisabled={step === 5}>
|
||||||
onClick={nextStep}
|
|
||||||
dimension="small"
|
|
||||||
isDisabled={step === 5}
|
|
||||||
>
|
|
||||||
Next step
|
Next step
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,7 +3,9 @@ export function ContentWrapperFix() {
|
|||||||
<>
|
<>
|
||||||
<style
|
<style
|
||||||
/* eslint-disable-next-line react/no-danger */
|
/* eslint-disable-next-line react/no-danger */
|
||||||
dangerouslySetInnerHTML={{ __html: '#wpbody-content { padding-top: 73px; }' }}
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: '#wpbody-content { padding-top: 73px; }',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<style
|
<style
|
||||||
|
@ -20,7 +20,14 @@ function Steps({ count, current, titles }: Props) {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="mailpoet-step-badge">{i >= current ? i : ''}</div>
|
<div className="mailpoet-step-badge">{i >= current ? i : ''}</div>
|
||||||
{titles[i - 1] && <div className="mailpoet-step-title" data-title={titles[i - 1] || ''}>{titles[i - 1] || ''}</div>}
|
{titles[i - 1] && (
|
||||||
|
<div
|
||||||
|
className="mailpoet-step-title"
|
||||||
|
data-title={titles[i - 1] || ''}
|
||||||
|
>
|
||||||
|
{titles[i - 1] || ''}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<ContentWrapperFix />
|
<ContentWrapperFix />
|
||||||
|
@ -8,11 +8,14 @@ type Props = {
|
|||||||
cacheCalculation: string;
|
cacheCalculation: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SubscribersCacheMessage({ cacheCalculation }: Props): JSX.Element {
|
export function SubscribersCacheMessage({
|
||||||
|
cacheCalculation,
|
||||||
|
}: Props): JSX.Element {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [errors, setErrors] = useState([]);
|
const [errors, setErrors] = useState([]);
|
||||||
const datetimeDiff = new Date().getTime() - new Date(cacheCalculation).getTime();
|
const datetimeDiff =
|
||||||
const minutes = Math.floor((datetimeDiff / 1000) / 60);
|
new Date().getTime() - new Date(cacheCalculation).getTime();
|
||||||
|
const minutes = Math.floor(datetimeDiff / 1000 / 60);
|
||||||
|
|
||||||
const handleRecalculate = () => {
|
const handleRecalculate = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -20,9 +23,11 @@ export function SubscribersCacheMessage({ cacheCalculation }: Props): JSX.Elemen
|
|||||||
api_version: MailPoet.apiVersion,
|
api_version: MailPoet.apiVersion,
|
||||||
endpoint: 'settings',
|
endpoint: 'settings',
|
||||||
action: 'recalculateSubscribersCountsCache',
|
action: 'recalculateSubscribersCountsCache',
|
||||||
}).done(() => {
|
})
|
||||||
|
.done(() => {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}).fail((response:ErrorResponse) => {
|
})
|
||||||
|
.fail((response: ErrorResponse) => {
|
||||||
setErrors(response.errors.map((error) => error.message));
|
setErrors(response.errors.map((error) => error.message));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
@ -30,7 +35,6 @@ export function SubscribersCacheMessage({ cacheCalculation }: Props): JSX.Elemen
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-subscribers-cache-notice">
|
<div className="mailpoet-subscribers-cache-notice">
|
||||||
|
|
||||||
{ReactStringReplace(
|
{ReactStringReplace(
|
||||||
MailPoet.I18n.t('subscribersCountWereCalculatedWithMinutesAgo'),
|
MailPoet.I18n.t('subscribersCountWereCalculatedWithMinutesAgo'),
|
||||||
/<abbr>(.*?)<\/abbr>/,
|
/<abbr>(.*?)<\/abbr>/,
|
||||||
@ -52,7 +56,13 @@ export function SubscribersCacheMessage({ cacheCalculation }: Props): JSX.Elemen
|
|||||||
{MailPoet.I18n.t('recalculateNow')}
|
{MailPoet.I18n.t('recalculateNow')}
|
||||||
</Button>
|
</Button>
|
||||||
<div className="mailpoet-gap" />
|
<div className="mailpoet-gap" />
|
||||||
{errors.length > 0 && <Notice type="error">{errors.map((error) => <p key={error}>{error}</p>)}</Notice>}
|
{errors.length > 0 && (
|
||||||
|
<Notice type="error">
|
||||||
|
{errors.map((error) => (
|
||||||
|
<p key={error}>{error}</p>
|
||||||
|
))}
|
||||||
|
</Notice>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,16 @@ function SubscribersInPlan({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mailpoet-subscribers-in-plan">
|
<div className="mailpoet-subscribers-in-plan">
|
||||||
{ReactStringReplace(MailPoet.I18n.t('subscribersInPlan'), '%s', () => subscribersInPlanCount)}
|
{ReactStringReplace(
|
||||||
{' '}
|
MailPoet.I18n.t('subscribersInPlan'),
|
||||||
|
'%s',
|
||||||
|
() => subscribersInPlanCount,
|
||||||
|
)}{' '}
|
||||||
<HelpTooltip
|
<HelpTooltip
|
||||||
tooltip={MailPoet.I18n.t('subscribersInPlanTooltip')}
|
tooltip={MailPoet.I18n.t('subscribersInPlanTooltip')}
|
||||||
place="right"
|
place="right"
|
||||||
/>
|
/>
|
||||||
<span className="mailpoet-subscribers-in-plan-spacer">{' '}</span>
|
<span className="mailpoet-subscribers-in-plan-spacer"> </span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user