mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-04 08:47:23 +08:00
Merge branch 'trunk' into PCP-4077-conditionally-display-payment-methods
This commit is contained in:
commit
dde1a702ea
135 changed files with 2297 additions and 2246 deletions
|
@ -45,7 +45,7 @@
|
|||
}
|
||||
|
||||
@mixin disabled-state($control-type) {
|
||||
.components-#{$control-type}-control.is-disabled {
|
||||
.components-#{$control-type}-control.ppcp--is-disabled {
|
||||
.components-#{$control-type}-control__input,
|
||||
.components-#{$control-type}-control__label,
|
||||
.components-base-control__help {
|
||||
|
|
|
@ -16,6 +16,7 @@ $color-text-tertiary: #505050;
|
|||
$color-text-text: #070707;
|
||||
$color-border: #AEAEAE;
|
||||
$color-divider: #F0F0F0;
|
||||
$color-error-red: #cc1818;
|
||||
|
||||
$shadow-card: 0 3px 6px 0 rgba(0, 0, 0, 0.15);
|
||||
$shadow-selection-box: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
|
||||
|
@ -61,4 +62,13 @@ $card-vertical-gap: 48px;
|
|||
--color-text-main: #{$color-text-text};
|
||||
--color-text-teriary: #{$color-text-tertiary};
|
||||
--color-text-description: #{$color-gray-700};
|
||||
--color-error: #{$color-error-red};
|
||||
|
||||
// Default settings-block theme.
|
||||
--block-item-gap: 16px;
|
||||
--block-header-gap: 6px;
|
||||
--block-separator-gap: 32px;
|
||||
--block-separator-size: 1px;
|
||||
--block-separator-color: var(--color-gray-200);
|
||||
--block-action-gap: 16px; // Space between two consecutive action blocks.
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
@import './reusable-components/badge-box';
|
||||
@import './reusable-components/busy-state';
|
||||
@import './reusable-components/button';
|
||||
@import './reusable-components/elements';
|
||||
@import './reusable-components/fields';
|
||||
@import './reusable-components/hstack';
|
||||
@import './reusable-components/navigation';
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
&__toggler {
|
||||
.ppcp--toggler {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -10,19 +10,38 @@
|
|||
border: 0;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
margin: 24px auto;
|
||||
text-align: var(--accordion-text-align, center);
|
||||
width: var(--accordion-width, auto);
|
||||
margin: var(--accordion-toggler-gap, 24px) auto;
|
||||
}
|
||||
|
||||
&__title-wrapper {
|
||||
@include font(14, 32, 450);
|
||||
color: $color-gray-900;
|
||||
|
||||
.ppcp--title-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
margin: 24px 0 0;
|
||||
.ppcp--accordion-content {
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
transition: grid-template-rows 0.2s ease-out, opacity 0.2s ease-out, margin 0.2s ease-out;
|
||||
opacity: 0;
|
||||
margin: 0;
|
||||
|
||||
> .ppcp--content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.ppcp--is-open {
|
||||
grid-template-rows: 1fr;
|
||||
opacity: 1;
|
||||
margin: 24px 0 0;
|
||||
transition: grid-template-rows 0.3s ease-in, opacity 0.3s ease-in, margin 0.3s ease-in;
|
||||
|
||||
> .ppcp--content {
|
||||
// Show the overflow, since the focus-outline can extend outside the content div.
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* General styling for reusable components in the "Elements" folder.
|
||||
*/
|
||||
|
||||
.ppcp--header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-bottom: var(--block-header-gap, 6px);
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp--title {
|
||||
@include font(11, 22, 600);
|
||||
color: var(--color-text-title);
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.ppcp--no-caps {
|
||||
@include font(14, 16, 600);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
&.ppcp--big {
|
||||
@include font(16, 20, 600);
|
||||
}
|
||||
|
||||
.ppcp-r-title-badge {
|
||||
text-transform: none;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp--title-extra {
|
||||
@include font(13, 20, 400);
|
||||
color: var(--color-text-teriary);
|
||||
text-transform: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.ppcp--title-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ppcp--description {
|
||||
@include font(13, 20, 400);
|
||||
margin: 0;
|
||||
color: var(--color-text-description);
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-blueberry);
|
||||
}
|
||||
|
||||
strong {
|
||||
color: var(--color-gray-800);
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp--action {
|
||||
& + .ppcp--action {
|
||||
margin-top: var(--block-action-gap, 16px);
|
||||
}
|
||||
}
|
|
@ -1,20 +1,31 @@
|
|||
.components-flex {
|
||||
display: flex;
|
||||
-webkit-box-align: stretch;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
-webkit-box-pack: center;
|
||||
justify-content: center;
|
||||
.ppcp-r-app {
|
||||
.components-flex {
|
||||
display: flex;
|
||||
-webkit-box-align: stretch;
|
||||
align-items: stretch;
|
||||
-webkit-box-pack: center;
|
||||
|
||||
.components-h-stack {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
gap: 32px;
|
||||
.components-h-stack {
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
.components-v-stack {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// Fix layout for checkboxes inside a flex-stack.
|
||||
.components-checkbox-control > .components-base-control__field > .components-flex {
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix layout for checkboxes inside a flex-stack.
|
||||
.components-checkbox-control >.components-base-control__field > .components-flex {
|
||||
.ppcp--horizontal {
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,11 +48,11 @@
|
|||
@include font(16, 24, 600);
|
||||
color: var(--color-text);
|
||||
|
||||
.title {
|
||||
.ppcp--nav-title {
|
||||
margin-left: 18px;
|
||||
}
|
||||
|
||||
.big {
|
||||
.ppcp--big {
|
||||
@include font(20, 28, 400);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.is-scrolled {
|
||||
&.ppcp--is-scrolled {
|
||||
box-shadow: 0 -1px 0 0 $color-gray-300 inset, 0 8px 8px 0 rgba(85, 93, 102, .3);
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@
|
|||
row-gap: 8px;
|
||||
white-space: nowrap;
|
||||
|
||||
&--right {
|
||||
.ppcp-r-navigation--right {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
|
@ -98,12 +98,12 @@
|
|||
box-shadow: -5px 0 8px var(--ppcp-color-app-bg);
|
||||
}
|
||||
|
||||
&--progress-bar {
|
||||
.ppcp-r-navigation--progress-bar {
|
||||
height: 2px;
|
||||
}
|
||||
|
||||
.components-button.is-title {
|
||||
.title {
|
||||
.ppcp--title {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
padding: 24px 16px 24px 16px;
|
||||
|
||||
|
||||
&.selected {
|
||||
&.ppcp--selected {
|
||||
border-color: $color-blueberry;
|
||||
outline-color: $color-blueberry;
|
||||
box-shadow: $shadow-selection-box;
|
||||
|
|
|
@ -6,50 +6,8 @@
|
|||
flex-direction: column;
|
||||
gap: var(--block-item-gap, 16px);
|
||||
|
||||
&.ppcp-r-settings-block__input,
|
||||
&.ppcp-r-settings-block__select {
|
||||
gap: 6px 0;
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-bottom: var(--block-header-gap, 6px);
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__title {
|
||||
@include font(11, 22, 600);
|
||||
color: var(--color-text-title);
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
|
||||
&.style-alt {
|
||||
@include font(14, 16, 600);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
&.style-big {
|
||||
@include font(16, 20, 600);
|
||||
}
|
||||
|
||||
.ppcp-r-title-badge {
|
||||
text-transform: none;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__title-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.ppcp-r-settings-block__feature {
|
||||
.ppcp-r-settings-block__title {
|
||||
.ppcp--title {
|
||||
@include font(13, 20, 600);
|
||||
color: var(--color-text-main);
|
||||
text-transform: none;
|
||||
|
@ -61,98 +19,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.ppcp-r-settings-block__toggle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.ppcp-r-settings-block__title {
|
||||
@include font(13, 20, 400);
|
||||
color: var(--color-text-main);
|
||||
text-transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__description {
|
||||
@include font(13, 20, 400);
|
||||
margin: 0;
|
||||
color: var(--color-text-description);
|
||||
|
||||
&:not(:last-child) {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color-blueberry);
|
||||
}
|
||||
|
||||
strong {
|
||||
color: var(--color-gray-800);
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__supplementary-title-label {
|
||||
@include font(13, 20, 400);
|
||||
color: var(--color-text-teriary);
|
||||
text-transform: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__action {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.components-flex {
|
||||
row-gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
+ .ppcp-r-settings-block:not(.no-gap) {
|
||||
+ .ppcp-r-settings-block:not(.ppcp--no-gap) {
|
||||
margin-top: var(--block-separator-gap, 32px);
|
||||
padding-top: var(--block-separator-gap, 32px);
|
||||
border-top: 1px solid var(--color-gray-200);
|
||||
}
|
||||
|
||||
// Types
|
||||
&--toggle-content {
|
||||
&.ppcp-r-settings-block--content-visible {
|
||||
.ppcp-r-settings-block__toggle-content {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__header {
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--sandbox-connected {
|
||||
.ppcp-r-settings-block__content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.ppcp-r-connection-status__data {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
&--connect-sandbox {
|
||||
button.components-button {
|
||||
@include small-button;
|
||||
}
|
||||
|
||||
.ppcp-r__radio-content-additional {
|
||||
@include vertical-layout-event-gap(24px);
|
||||
align-items: flex-start;
|
||||
|
||||
.ppcp-r-vertical-text-control,
|
||||
input[type='text'] {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
border-top: var(--block-separator-size, 1px) solid var(--block-separator-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,28 +37,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block--toggle-content {
|
||||
.ppcp-r-settings-block__content {
|
||||
margin-top: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 50px;
|
||||
}
|
||||
|
||||
.ppcp-r-settings-block__accordion {
|
||||
> .ppcp-r-accordion {
|
||||
width: 100%;
|
||||
|
||||
.ppcp-r-accordion__toggler {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
text-align: unset;
|
||||
}
|
||||
.ppcp-r-accordion {
|
||||
--accordion-width: 100%;
|
||||
--accordion-toggler-gap: 0;
|
||||
--accordion-text-align: left;
|
||||
|
||||
.ppcp--title-wrapper {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,12 +40,15 @@
|
|||
gap: 24px;
|
||||
}
|
||||
|
||||
.ppcp-r-settings-card__content {
|
||||
.ppcp--content {
|
||||
flex: 1;
|
||||
max-width: var(--card-width-content);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: 4px;
|
||||
padding: 24px;
|
||||
|
||||
&.ppcp--is-card {
|
||||
max-width: var(--card-width-content);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: 4px;
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp-r-settings-card__title {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@import './settings/controls';
|
||||
@import './settings/input';
|
||||
@import './settings/connection-status';
|
||||
@import './settings/tab-settings';
|
||||
@import './settings/tab-styling';
|
||||
@import './settings/tab-paylater-configurator';
|
||||
|
||||
|
|
|
@ -21,13 +21,18 @@
|
|||
}
|
||||
|
||||
.client-id-error {
|
||||
color: #cc1818;
|
||||
margin: -16px 0 24px;
|
||||
@include font(11, 16, 450);
|
||||
margin: -16px 0 24px;
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.onboarding-advanced-options {
|
||||
margin-top: 24px;
|
||||
max-width: 800px;
|
||||
|
||||
.ppcp--toggler .ppcp--title-wrapper {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
// Connection Status
|
||||
.ppcp-r-connection-status {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
|
||||
&__status-status {
|
||||
margin: 0 0 8px 0;
|
||||
|
||||
strong {
|
||||
@include font(14, 24, 700);
|
||||
color: $color-black;
|
||||
}
|
||||
}
|
||||
|
||||
&__status-label {
|
||||
@include font(11, 22, 600);
|
||||
color: $color-gray-900;
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&__status-value {
|
||||
@include font(13, 26, 400);
|
||||
color: $color-text-tertiary;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&__data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__status-toggle--toggled {
|
||||
.ppcp-r-connection-status__show-all-data {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
&__status-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
strong {
|
||||
@include font(14, 24, 600);
|
||||
color: $color-gray-800;
|
||||
margin-right: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ppcp-r-connection-status__status-toggle {
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 767px) {
|
||||
flex-wrap: wrap;
|
||||
&__status {
|
||||
width: 100%;
|
||||
}
|
||||
&__status-row {
|
||||
flex-wrap: wrap;
|
||||
|
||||
strong {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
span {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
.ppcp--static-value {
|
||||
@include font(13, 26, 400);
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// Fields
|
||||
.ppcp-r {
|
||||
.ppcp-r-app {
|
||||
input[type='text'] {
|
||||
border-color: $color-gray-700;
|
||||
width: 100%;
|
||||
|
@ -13,13 +13,13 @@
|
|||
}
|
||||
|
||||
// MultiSelect control
|
||||
.ppcp-r {
|
||||
&__radio-wrapper {
|
||||
.ppcp-r-app {
|
||||
.ppcp-r__radio-wrapper {
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
&__radio-content {
|
||||
.ppcp-r__radio-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
@ -29,49 +29,47 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__radio-content-additional {
|
||||
.ppcp-r__radio-content-additional {
|
||||
padding-left: 32px;
|
||||
}
|
||||
|
||||
// Select control styles
|
||||
&__control {
|
||||
border-radius: 2px;
|
||||
border-color: $color-gray-700;
|
||||
min-height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__input-container {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__value-container {
|
||||
padding: 0 0 0 7px;
|
||||
}
|
||||
|
||||
&__indicator {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
&__indicator-separator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__value-container--has-value {
|
||||
.ppcp-r__single-value {
|
||||
color: $color-gray-800;
|
||||
// Styles for "react-select" (see `Fields/Select.js`)
|
||||
.ppcp-r-select {
|
||||
.ppcp__control {
|
||||
border-radius: 2px;
|
||||
border-color: $color-gray-700;
|
||||
min-height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__placeholder,
|
||||
&__single-value {
|
||||
@include font(13, 20, 400);
|
||||
}
|
||||
.ppcp__input-container {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__option {
|
||||
&--is-selected {
|
||||
background-color: $color-gray-200;
|
||||
.ppcp__value-container {
|
||||
padding: 0 0 0 7px;
|
||||
}
|
||||
|
||||
.ppcp__indicator {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.ppcp__value-container--has-value {
|
||||
.ppcp__single-value {
|
||||
color: $color-gray-800;
|
||||
}
|
||||
}
|
||||
|
||||
.ppcp__placeholder,
|
||||
.ppcp__single-value {
|
||||
@include font(13, 20, 400);
|
||||
}
|
||||
|
||||
.ppcp__option {
|
||||
.ppcp__option--is-selected {
|
||||
background-color: $color-gray-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* Used by the "Connection Details" section in the top of the "Settings" tab.
|
||||
*/
|
||||
.ppcp--value-list {
|
||||
--block-item-gap: 0;
|
||||
--block-separator-gap: 6px;
|
||||
--block-header-gap: 0;
|
||||
--block-separator-size: 0;
|
||||
}
|
|
@ -33,7 +33,7 @@
|
|||
}
|
||||
|
||||
// Select-fields have a smaller gap between the header and input field.
|
||||
&.has-select {
|
||||
&.ppcp--has-select {
|
||||
--block-header-gap: 8px;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@
|
|||
margin-bottom: 28px;
|
||||
border-bottom: 1px solid var(--color-separators);
|
||||
|
||||
&.no-gap,
|
||||
&.ppcp--no-gap,
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
import { useEffect, useMemo } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { OnboardingHooks, CommonHooks } from '../data';
|
||||
import SpinnerOverlay from './ReusableComponents/SpinnerOverlay';
|
||||
import SendOnlyMessage from './Screens/SendOnlyMessage';
|
||||
import OnboardingScreen from './Screens/Onboarding';
|
||||
import SettingsScreen from './Screens/Settings';
|
||||
import { initStore as initSettingsStore } from '../data/settings-tab';
|
||||
import { useSettingsState } from '../data/settings-tab/hooks';
|
||||
|
||||
// Initialize the settings store
|
||||
initSettingsStore();
|
||||
|
||||
const SettingsApp = () => {
|
||||
const onboardingProgress = OnboardingHooks.useSteps();
|
||||
const { isReady: settingsIsReady } = useSettingsState();
|
||||
const { isReady: onboardingIsReady, completed: onboardingCompleted } =
|
||||
OnboardingHooks.useSteps();
|
||||
const {
|
||||
isReady: merchantIsReady,
|
||||
merchant: { isSendOnlyCountry },
|
||||
|
@ -33,34 +28,28 @@ const SettingsApp = () => {
|
|||
}, [] );
|
||||
|
||||
const wrapperClass = classNames( 'ppcp-r-app', {
|
||||
loading: ! onboardingProgress.isReady || ! settingsIsReady,
|
||||
loading: ! onboardingIsReady,
|
||||
} );
|
||||
|
||||
const Content = useMemo( () => {
|
||||
if (
|
||||
! onboardingProgress.isReady ||
|
||||
! merchantIsReady ||
|
||||
! settingsIsReady
|
||||
) {
|
||||
return (
|
||||
<SpinnerOverlay
|
||||
message={ __( 'Loading…', 'woocommerce-paypal-payments' ) }
|
||||
/>
|
||||
);
|
||||
if ( ! onboardingIsReady || ! merchantIsReady ) {
|
||||
return <SpinnerOverlay />;
|
||||
}
|
||||
|
||||
if ( isSendOnlyCountry ) {
|
||||
return <SendOnlyMessage />;
|
||||
}
|
||||
if ( ! onboardingProgress.completed ) {
|
||||
|
||||
if ( ! onboardingCompleted ) {
|
||||
return <OnboardingScreen />;
|
||||
}
|
||||
|
||||
return <SettingsScreen />;
|
||||
}, [
|
||||
isSendOnlyCountry,
|
||||
merchantIsReady,
|
||||
onboardingProgress.completed,
|
||||
onboardingProgress.isReady,
|
||||
settingsIsReady,
|
||||
onboardingCompleted,
|
||||
onboardingIsReady,
|
||||
] );
|
||||
|
||||
return <div className={ wrapperClass }>{ Content }</div>;
|
||||
|
|
|
@ -1,53 +1,33 @@
|
|||
import { Icon } from '@wordpress/components';
|
||||
import { chevronDown, chevronUp } from '@wordpress/icons';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { useAccordionState } from '../../hooks/useAccordionState';
|
||||
|
||||
// Provide defaults for all layout components so the generic version just works.
|
||||
const DefaultHeader = ( { children, className = '' } ) => (
|
||||
<div className={ `ppcp-r-accordion__header ${ className }`.trim() }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
const DefaultTitleWrapper = ( { children } ) => (
|
||||
<div className="ppcp-r-accordion__title-wrapper">{ children }</div>
|
||||
);
|
||||
const DefaultTitle = ( { children } ) => (
|
||||
<span className="ppcp-r-accordion__title">{ children }</span>
|
||||
);
|
||||
const DefaultAction = ( { children } ) => (
|
||||
<span className="ppcp-r-accordion__action">{ children }</span>
|
||||
);
|
||||
const DefaultDescription = ( { children } ) => (
|
||||
<div className="ppcp-r-accordion__description">{ children }</div>
|
||||
);
|
||||
|
||||
const AccordionContent = ( { isOpen, children } ) => {
|
||||
if ( ! isOpen || ! children ) {
|
||||
return null;
|
||||
}
|
||||
return <div className="ppcp-r-accordion__content">{ children }</div>;
|
||||
};
|
||||
import {
|
||||
Content,
|
||||
Description,
|
||||
Header,
|
||||
Title,
|
||||
Action,
|
||||
TitleWrapper,
|
||||
} from './Elements';
|
||||
|
||||
const Accordion = ( {
|
||||
title,
|
||||
id = '',
|
||||
noCaps = false,
|
||||
initiallyOpen = null,
|
||||
description = '',
|
||||
children = null,
|
||||
className = '',
|
||||
|
||||
// Layout components can be overridden by the caller
|
||||
Header = DefaultHeader,
|
||||
TitleWrapper = DefaultTitleWrapper,
|
||||
Title = DefaultTitle,
|
||||
Action = DefaultAction,
|
||||
Description = DefaultDescription,
|
||||
} ) => {
|
||||
const { isOpen, toggleOpen } = useAccordionState( { id, initiallyOpen } );
|
||||
const wrapperClasses = classNames( 'ppcp-r-accordion', className, {
|
||||
'ppcp--is-open': isOpen,
|
||||
} );
|
||||
const contentClass = classNames( 'ppcp--accordion-content', {
|
||||
'ppcp--is-open': isOpen,
|
||||
} );
|
||||
|
||||
const icon = isOpen ? chevronUp : chevronDown;
|
||||
|
||||
|
@ -55,22 +35,22 @@ const Accordion = ( {
|
|||
<div className={ wrapperClasses } { ...( id && { id } ) }>
|
||||
<button
|
||||
type="button"
|
||||
className="ppcp-r-accordion__toggler"
|
||||
className="ppcp--toggler"
|
||||
onClick={ toggleOpen }
|
||||
>
|
||||
<Header>
|
||||
<TitleWrapper>
|
||||
<Title>{ title }</Title>
|
||||
<Title noCaps={ noCaps }>{ title }</Title>
|
||||
<Action>
|
||||
<Icon icon={ icon } />
|
||||
</Action>
|
||||
</TitleWrapper>
|
||||
{ description && (
|
||||
<Description>{ description }</Description>
|
||||
) }
|
||||
<Description>{ description }</Description>
|
||||
</Header>
|
||||
</button>
|
||||
<AccordionContent isOpen={ isOpen }>{ children }</AccordionContent>
|
||||
<div className={ contentClass }>
|
||||
<Content asCard={ false }>{ children }</Content>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { CommonHooks } from '../../data';
|
||||
|
||||
const ConnectionInfo = () => {
|
||||
const { merchant } = CommonHooks.useMerchantInfo();
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-connection-status__data">
|
||||
<StatusRow
|
||||
label={ __( 'Merchant ID', 'woocommerce-paypal-payments' ) }
|
||||
value={ merchant.id }
|
||||
/>
|
||||
<StatusRow
|
||||
label={ __( 'Email address', 'woocommerce-paypal-payments' ) }
|
||||
value={ merchant.email }
|
||||
/>
|
||||
<StatusRow
|
||||
label={ __( 'Client ID', 'woocommerce-paypal-payments' ) }
|
||||
value={ merchant.clientId }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default ConnectionInfo;
|
||||
|
||||
const StatusRow = ( { label, value } ) => (
|
||||
<div className="ppcp-r-connection-status__status-row">
|
||||
<span className="ppcp-r-connection-status__status-label">
|
||||
{ label }
|
||||
</span>
|
||||
<span className="ppcp-r-connection-status__status-value">
|
||||
{ value }
|
||||
</span>
|
||||
</div>
|
||||
);
|
|
@ -0,0 +1,18 @@
|
|||
import { Button } from '@wordpress/components';
|
||||
|
||||
import { Action } from '../Elements';
|
||||
|
||||
const ControlButton = ( {
|
||||
type = 'secondary',
|
||||
isBusy,
|
||||
onClick,
|
||||
buttonLabel,
|
||||
} ) => (
|
||||
<Action>
|
||||
<Button isBusy={ isBusy } variant={ type } onClick={ onClick }>
|
||||
{ buttonLabel }
|
||||
</Button>
|
||||
</Action>
|
||||
);
|
||||
|
||||
export default ControlButton;
|
|
@ -0,0 +1,14 @@
|
|||
import { Action } from '../Elements';
|
||||
import { RadioGroup } from '../Fields';
|
||||
|
||||
const ControlRadioGroup = ( { options, value, onChange } ) => (
|
||||
<Action>
|
||||
<RadioGroup
|
||||
options={ options }
|
||||
selected={ value }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
</Action>
|
||||
);
|
||||
|
||||
export default ControlRadioGroup;
|
|
@ -0,0 +1,22 @@
|
|||
import { Select } from '../Fields';
|
||||
import { Action } from '../Elements';
|
||||
|
||||
const ControlSelect = ( {
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
isMulti = false,
|
||||
} ) => (
|
||||
<Action>
|
||||
<Select
|
||||
isMulti={ isMulti }
|
||||
options={ options }
|
||||
value={ value }
|
||||
placeholder={ placeholder }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
</Action>
|
||||
);
|
||||
|
||||
export default ControlSelect;
|
|
@ -0,0 +1,9 @@
|
|||
import { Action } from '../Elements';
|
||||
|
||||
const ControlStaticValue = ( { value } ) => (
|
||||
<Action>
|
||||
<div className="ppcp--static-value">{ value }</div>
|
||||
</Action>
|
||||
);
|
||||
|
||||
export default ControlStaticValue;
|
|
@ -0,0 +1,22 @@
|
|||
import { TextControl } from '@wordpress/components';
|
||||
|
||||
import { Action, Description } from '../Elements';
|
||||
|
||||
const ControlTextInput = ( {
|
||||
value,
|
||||
description,
|
||||
onChange,
|
||||
placeholder = '',
|
||||
} ) => (
|
||||
<Action>
|
||||
<TextControl
|
||||
className="ppcp-r-vertical-text-control"
|
||||
placeholder={ placeholder }
|
||||
value={ value }
|
||||
onChange={ onChange }
|
||||
/>
|
||||
<Description>{ description }</Description>
|
||||
</Action>
|
||||
);
|
||||
|
||||
export default ControlTextInput;
|
|
@ -0,0 +1,19 @@
|
|||
import { ToggleControl } from '@wordpress/components';
|
||||
import { Action, Description } from '../Elements';
|
||||
|
||||
const ControlToggleButton = ( { label, description, value, onChange } ) => (
|
||||
<Action>
|
||||
<ToggleControl
|
||||
className="ppcp--control-toggle"
|
||||
__nextHasNoMarginBottom={ true }
|
||||
checked={ value }
|
||||
onChange={ onChange }
|
||||
label={ label }
|
||||
help={
|
||||
description ? <Description>{ description }</Description> : null
|
||||
}
|
||||
/>
|
||||
</Action>
|
||||
);
|
||||
|
||||
export default ControlToggleButton;
|
|
@ -0,0 +1,6 @@
|
|||
export { default as ControlStaticValue } from './ControlStaticValue';
|
||||
export { default as ControlTextInput } from './ControlTextInput';
|
||||
export { default as ControlToggleButton } from './ControlToggleButton';
|
||||
export { default as ControlButton } from './ControlButton';
|
||||
export { default as ControlRadioGroup } from './ControlRadioGroup';
|
||||
export { default as ControlSelect } from './ControlSelect';
|
|
@ -0,0 +1,5 @@
|
|||
const Action = ( { children } ) => (
|
||||
<div className="ppcp--action">{ children }</div>
|
||||
);
|
||||
|
||||
export default Action;
|
|
@ -0,0 +1,15 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
const Content = ( { children, asCard = true, className = '', id = '' } ) => {
|
||||
const elementClasses = classNames( 'ppcp--content', className, {
|
||||
'ppcp--is-card': asCard,
|
||||
} );
|
||||
|
||||
return (
|
||||
<div id={ id } className={ elementClasses }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Content;
|
|
@ -0,0 +1,5 @@
|
|||
const ContentWrapper = ( { children } ) => (
|
||||
<div className="ppcp-r-settings-card__content-wrapper">{ children }</div>
|
||||
);
|
||||
|
||||
export default ContentWrapper;
|
|
@ -0,0 +1,23 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
const Description = ( { children, className = '' } ) => {
|
||||
// Don't output anything if description is empty.
|
||||
if ( ! children ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const elementClasses = classNames( 'ppcp--description', className );
|
||||
|
||||
if ( 'string' !== typeof children ) {
|
||||
return <span className={ elementClasses }>{ children }</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={ elementClasses }
|
||||
dangerouslySetInnerHTML={ { __html: children } }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Description;
|
|
@ -0,0 +1,13 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
const Header = ( { children, className = '' } ) => {
|
||||
if ( ! children ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const elementClasses = classNames( 'ppcp--header', className );
|
||||
|
||||
return <div className={ elementClasses }>{ children }</div>;
|
||||
};
|
||||
|
||||
export default Header;
|
|
@ -0,0 +1,16 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
const Title = ( { children, noCaps = false, big = false, className = '' } ) => {
|
||||
if ( ! children ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const elementClasses = classNames( 'ppcp--title', className, {
|
||||
'ppcp--no-caps': noCaps,
|
||||
'ppcp--big': big,
|
||||
} );
|
||||
|
||||
return <span className={ elementClasses }>{ children }</span>;
|
||||
};
|
||||
|
||||
export default Title;
|
|
@ -0,0 +1,9 @@
|
|||
const TitleExtra = ( { children } ) => {
|
||||
if ( ! children ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <span className="ppcp--title-extra">{ children }</span>;
|
||||
};
|
||||
|
||||
export default TitleExtra;
|
|
@ -0,0 +1,5 @@
|
|||
const TitleWrapper = ( { children } ) => (
|
||||
<span className="ppcp--title-wrapper">{ children }</span>
|
||||
);
|
||||
|
||||
export default TitleWrapper;
|
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* Static elements used to build UI layouts.
|
||||
*/
|
||||
|
||||
export { default as Action } from './Action';
|
||||
export { default as Content } from './Content';
|
||||
export { default as ContentWrapper } from './ContentWrapper';
|
||||
export { default as Description } from './Description';
|
||||
export { default as Header } from './Header';
|
||||
export { default as Title } from './Title';
|
||||
export { default as TitleExtra } from './TitleExtra';
|
||||
export { default as TitleWrapper } from './TitleWrapper';
|
||||
export { default as Separator } from './Separator';
|
|
@ -1,138 +0,0 @@
|
|||
import { CheckboxControl } from '@wordpress/components';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const PayPalCheckbox = ( {
|
||||
currentValue,
|
||||
label,
|
||||
value,
|
||||
checked = null,
|
||||
disabled = null,
|
||||
changeCallback,
|
||||
} ) => {
|
||||
let isChecked = checked;
|
||||
|
||||
if ( null === isChecked ) {
|
||||
if ( Array.isArray( currentValue ) ) {
|
||||
isChecked = currentValue.includes( value );
|
||||
} else {
|
||||
isChecked = currentValue;
|
||||
}
|
||||
}
|
||||
|
||||
const className = classNames( { 'is-disabled': disabled } );
|
||||
|
||||
const onChange = ( newState ) => {
|
||||
let newValue;
|
||||
|
||||
if ( ! Array.isArray( currentValue ) ) {
|
||||
newValue = newState;
|
||||
} else if ( newState ) {
|
||||
newValue = [ ...currentValue, value ];
|
||||
} else {
|
||||
newValue = currentValue.filter(
|
||||
( optionValue ) => optionValue !== value
|
||||
);
|
||||
}
|
||||
|
||||
changeCallback( newValue );
|
||||
};
|
||||
|
||||
return (
|
||||
<CheckboxControl
|
||||
label={ label }
|
||||
value={ value }
|
||||
checked={ isChecked }
|
||||
disabled={ disabled }
|
||||
onChange={ onChange }
|
||||
className={ className }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const CheckboxGroup = ( { options, value, onChange } ) => (
|
||||
<>
|
||||
{ options.map( ( checkbox ) => (
|
||||
<PayPalCheckbox
|
||||
key={ checkbox.value }
|
||||
label={ checkbox.label }
|
||||
value={ checkbox.value }
|
||||
checked={ checkbox.checked }
|
||||
disabled={ checkbox.disabled }
|
||||
description={ checkbox.description }
|
||||
tooltip={ checkbox.tooltip }
|
||||
currentValue={ value }
|
||||
changeCallback={ onChange }
|
||||
/>
|
||||
) ) }
|
||||
</>
|
||||
);
|
||||
|
||||
export const PayPalRdb = ( {
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
currentValue,
|
||||
handleRdbState,
|
||||
} ) => {
|
||||
return (
|
||||
<div className="ppcp-r__radio">
|
||||
{ /* todo: Can we remove the wrapper div? */ }
|
||||
<input
|
||||
className="ppcp-r__radio-value"
|
||||
type="radio"
|
||||
id={ id }
|
||||
checked={ value === currentValue }
|
||||
name={ name }
|
||||
value={ value }
|
||||
onChange={ () => handleRdbState( value ) }
|
||||
/>
|
||||
<span className="ppcp-r__radio-presentation"></span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const PayPalRdbWithContent = ( {
|
||||
className,
|
||||
id,
|
||||
name,
|
||||
label,
|
||||
description,
|
||||
value,
|
||||
currentValue,
|
||||
handleRdbState,
|
||||
toggleAdditionalContent,
|
||||
children,
|
||||
} ) => {
|
||||
const wrapperClasses = classNames( 'ppcp-r__radio-wrapper', className );
|
||||
|
||||
return (
|
||||
<div className="ppcp-r__radio-outer-wrapper">
|
||||
<div className={ wrapperClasses }>
|
||||
<PayPalRdb
|
||||
id={ id }
|
||||
name={ name }
|
||||
value={ value }
|
||||
currentValue={ currentValue }
|
||||
handleRdbState={ handleRdbState }
|
||||
/>
|
||||
|
||||
<div className="ppcp-r__radio-content">
|
||||
<label htmlFor={ id }>{ label }</label>
|
||||
{ description && (
|
||||
<p
|
||||
className="ppcp-r__radio-description"
|
||||
dangerouslySetInnerHTML={ {
|
||||
__html: description,
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
{ toggleAdditionalContent && children && value === currentValue && (
|
||||
<div className="ppcp-r__radio-content-additional">
|
||||
{ children }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import { CheckboxControl } from '@wordpress/components';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const Checkbox = ( {
|
||||
label,
|
||||
value,
|
||||
checked = null,
|
||||
disabled = null,
|
||||
onChange,
|
||||
changeCallback, // deprecated.
|
||||
} ) => {
|
||||
const className = classNames( { 'ppcp--is-disabled': disabled } );
|
||||
|
||||
const handleChange = ( isChecked ) => {
|
||||
if ( onChange ) {
|
||||
onChange( value, isChecked );
|
||||
} else if ( changeCallback ) {
|
||||
console.warn(
|
||||
'Deprecated prop, use "onChange" instead of "changeCallback"'
|
||||
);
|
||||
changeCallback( value, isChecked );
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<CheckboxControl
|
||||
label={ label }
|
||||
value={ value }
|
||||
checked={ checked }
|
||||
disabled={ disabled }
|
||||
onChange={ handleChange }
|
||||
className={ className }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Checkbox;
|
|
@ -0,0 +1,42 @@
|
|||
import { PayPalCheckbox } from './index';
|
||||
|
||||
const CheckboxGroup = ( { options, value, onChange } ) => {
|
||||
const handleChange = ( key, checked ) => {
|
||||
const getNewValue = () => {
|
||||
if ( checked ) {
|
||||
return [ ...value, key ];
|
||||
}
|
||||
return value.filter( ( val ) => val !== key );
|
||||
};
|
||||
|
||||
onChange( getNewValue() );
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{ options.map(
|
||||
( {
|
||||
value: itemValue,
|
||||
label,
|
||||
checked,
|
||||
disabled,
|
||||
description,
|
||||
tooltip,
|
||||
} ) => (
|
||||
<PayPalCheckbox
|
||||
key={ itemValue }
|
||||
value={ itemValue }
|
||||
label={ label }
|
||||
checked={ checked }
|
||||
disabled={ disabled }
|
||||
description={ description }
|
||||
tooltip={ tooltip }
|
||||
changeCallback={ handleChange }
|
||||
/>
|
||||
)
|
||||
) }
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CheckboxGroup;
|
|
@ -0,0 +1,100 @@
|
|||
import classNames from 'classnames';
|
||||
import { PayPalCheckbox, PayPalRdb } from './index';
|
||||
|
||||
const OptionSelector = ( {
|
||||
multiSelect = false,
|
||||
options,
|
||||
value,
|
||||
onChange,
|
||||
} ) => (
|
||||
<div className="ppcp-r-select-box-wrapper">
|
||||
{ options.map(
|
||||
( { value: itemValue, title, description, contents } ) => {
|
||||
let isSelected;
|
||||
|
||||
if ( Array.isArray( value ) ) {
|
||||
isSelected = value.includes( itemValue );
|
||||
} else {
|
||||
isSelected = value === itemValue;
|
||||
}
|
||||
|
||||
return (
|
||||
<OptionItem
|
||||
key={ itemValue }
|
||||
itemTitle={ title }
|
||||
itemDescription={ description }
|
||||
itemValue={ itemValue }
|
||||
onChange={ onChange }
|
||||
isMulti={ multiSelect }
|
||||
isSelected={ isSelected }
|
||||
>
|
||||
{ contents }
|
||||
</OptionItem>
|
||||
);
|
||||
}
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
|
||||
export default OptionSelector;
|
||||
|
||||
const OptionItem = ( {
|
||||
itemTitle,
|
||||
itemDescription,
|
||||
itemValue,
|
||||
onChange,
|
||||
isMulti,
|
||||
isSelected,
|
||||
children,
|
||||
} ) => {
|
||||
const boxClassName = classNames( 'ppcp-r-select-box', {
|
||||
'ppcp--selected': isSelected,
|
||||
} );
|
||||
|
||||
return (
|
||||
<div className={ boxClassName }>
|
||||
<InputField
|
||||
value={ itemValue }
|
||||
isRadio={ ! isMulti }
|
||||
onChange={ onChange }
|
||||
isSelected={ isSelected }
|
||||
/>
|
||||
|
||||
<div className="ppcp-r-select-box__content">
|
||||
<div className="ppcp-r-select-box__content-inner">
|
||||
<span className="ppcp-r-select-box__title">
|
||||
{ itemTitle }
|
||||
</span>
|
||||
<p className="ppcp-r-select-box__description">
|
||||
{ itemDescription }
|
||||
</p>
|
||||
{ children && (
|
||||
<div className="ppcp-r-select-box__additional-content">
|
||||
{ children }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const InputField = ( { value, onChange, isRadio, isSelected } ) => {
|
||||
if ( isRadio ) {
|
||||
return (
|
||||
<PayPalRdb
|
||||
value={ value }
|
||||
onChange={ onChange }
|
||||
checked={ isSelected }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<PayPalCheckbox
|
||||
value={ value }
|
||||
onChange={ onChange }
|
||||
checked={ isSelected }
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
import { useCallback } from '@wordpress/element';
|
||||
|
||||
const RadioButton = ( {
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
currentValue,
|
||||
checked = null, // alternative to currentValue.
|
||||
onChange,
|
||||
handleRdbState, // deprecated
|
||||
} ) => {
|
||||
const handleChange = useCallback( () => {
|
||||
if ( onChange ) {
|
||||
onChange( value );
|
||||
} else if ( handleRdbState ) {
|
||||
console.warn(
|
||||
'Deprecated prop, use "onChange" instead of "handleRdbState"'
|
||||
);
|
||||
handleRdbState( value );
|
||||
}
|
||||
}, [ handleRdbState, onChange, value ] );
|
||||
|
||||
const radioProps = {
|
||||
className: 'ppcp-r__radio-value',
|
||||
type: 'radio',
|
||||
onChange: handleChange,
|
||||
checked: null === checked ? value === currentValue : checked,
|
||||
id,
|
||||
name,
|
||||
value,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ppcp-r__radio">
|
||||
{ /* todo: Can we remove the wrapper div? */ }
|
||||
<input { ...radioProps } />
|
||||
<span className="ppcp-r__radio-presentation"></span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadioButton;
|
|
@ -0,0 +1,50 @@
|
|||
import classNames from 'classnames';
|
||||
import { PayPalRdb } from './index';
|
||||
|
||||
const RadioButtonWithContent = ( {
|
||||
className,
|
||||
id,
|
||||
name,
|
||||
label,
|
||||
description,
|
||||
value,
|
||||
currentValue,
|
||||
handleRdbState,
|
||||
toggleAdditionalContent,
|
||||
children,
|
||||
} ) => {
|
||||
const wrapperClasses = classNames( 'ppcp-r__radio-wrapper', className );
|
||||
|
||||
return (
|
||||
<div className="ppcp-r__radio-outer-wrapper">
|
||||
<div className={ wrapperClasses }>
|
||||
<PayPalRdb
|
||||
id={ id }
|
||||
name={ name }
|
||||
value={ value }
|
||||
currentValue={ currentValue }
|
||||
handleRdbState={ handleRdbState }
|
||||
/>
|
||||
|
||||
<div className="ppcp-r__radio-content">
|
||||
<label htmlFor={ id }>{ label }</label>
|
||||
{ description && (
|
||||
<p
|
||||
className="ppcp-r__radio-description"
|
||||
dangerouslySetInnerHTML={ {
|
||||
__html: description,
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
{ toggleAdditionalContent && children && value === currentValue && (
|
||||
<div className="ppcp-r__radio-content-additional">
|
||||
{ children }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadioButtonWithContent;
|
|
@ -0,0 +1,13 @@
|
|||
import { RadioControl } from '@wordpress/components';
|
||||
|
||||
const RadioGroup = ( { options, selected, onChange } ) => {
|
||||
return (
|
||||
<RadioControl
|
||||
options={ options }
|
||||
onChange={ onChange }
|
||||
selected={ selected }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default RadioGroup;
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* TODO: Replace this with the WordPress select control once V2 with multi-select is ready.
|
||||
*
|
||||
* This component has a lot of compatibility logic to (a) make the ReactSelect component look like
|
||||
* a WordPress select component, and (b) convert values from Redux-format (value-strings) to
|
||||
* ReactSelect values (objects containing value and label). When switching to the
|
||||
* SelectControl from `@wordpress/components`, we can remove a lot of this code.
|
||||
*
|
||||
* @see https://wordpress.github.io/gutenberg/?path=/story/components-customselectcontrol-v2--multiple-selection
|
||||
* @file
|
||||
*/
|
||||
|
||||
import { default as ReactSelect, components } from 'react-select';
|
||||
import { Icon } from '@wordpress/components';
|
||||
import { chevronDown, chevronUp } from '@wordpress/icons';
|
||||
import { useCallback, useEffect, useState } from '@wordpress/element';
|
||||
|
||||
const DropdownIndicator = ( props ) => (
|
||||
<components.DropdownIndicator { ...props }>
|
||||
<Icon icon={ props.selectProps.menuIsOpen ? chevronUp : chevronDown } />
|
||||
</components.DropdownIndicator>
|
||||
);
|
||||
|
||||
const IndicatorSeparator = () => null;
|
||||
|
||||
// Convert a plain value string/array to react-select objects.
|
||||
const toInternalValue = ( selected, options ) => {
|
||||
if ( Array.isArray( selected ) ) {
|
||||
return selected.map( ( value ) =>
|
||||
options.find( ( option ) => option.value === value )
|
||||
);
|
||||
}
|
||||
|
||||
return options.find( ( option ) => option.value === selected );
|
||||
};
|
||||
|
||||
// Convert react-select object(s) to a plain value string/array.
|
||||
const toStoreValue = ( selected ) => {
|
||||
if ( ! selected ) {
|
||||
return null;
|
||||
}
|
||||
if ( Array.isArray( selected ) ) {
|
||||
return selected.map( ( value ) => value.value );
|
||||
}
|
||||
return selected.value;
|
||||
};
|
||||
|
||||
const Select = ( { options, value, onChange, isMulti, placeholder } ) => {
|
||||
const [ internalValue, setInternalValue ] = useState(
|
||||
toInternalValue( value, options )
|
||||
);
|
||||
|
||||
const onInternalValueChange = useCallback(
|
||||
( selected ) => {
|
||||
setInternalValue( selected );
|
||||
|
||||
if ( Array.isArray( selected ) ) {
|
||||
return onChange( selected.map( ( option ) => option.id ) );
|
||||
}
|
||||
return onChange( selected.id );
|
||||
},
|
||||
[ onChange ]
|
||||
);
|
||||
|
||||
// Forward changes of the internal ReactSelect value to the onChange callback.
|
||||
useEffect( () => {
|
||||
onChange( toStoreValue( internalValue ) );
|
||||
}, [ internalValue, onChange ] );
|
||||
|
||||
return (
|
||||
<ReactSelect
|
||||
className="ppcp-r-select"
|
||||
classNamePrefix="ppcp"
|
||||
isMulti={ isMulti }
|
||||
options={ options }
|
||||
value={ internalValue }
|
||||
onChange={ onInternalValueChange }
|
||||
placeholder={ placeholder }
|
||||
components={ { DropdownIndicator, IndicatorSeparator } }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* Generic input fields.
|
||||
*/
|
||||
|
||||
export { default as PayPalCheckbox } from './Checkbox';
|
||||
export { default as CheckboxGroup } from './CheckboxGroup';
|
||||
export { default as RadioGroup } from './RadioGroup';
|
||||
export { default as PayPalRdb } from './RadioButton';
|
||||
export { default as PayPalRdbWithContent } from './RadioContent';
|
||||
export { default as OptionSelector } from './OptionSelector';
|
||||
export { default as Select } from './Select';
|
|
@ -1,68 +0,0 @@
|
|||
import data from '../../utils/data';
|
||||
import { PayPalCheckbox, PayPalRdb } from './Fields';
|
||||
|
||||
const SelectBox = ( props ) => {
|
||||
let boxClassName = 'ppcp-r-select-box';
|
||||
|
||||
if (
|
||||
props.value === props.currentValue ||
|
||||
( Array.isArray( props.currentValue ) &&
|
||||
props.currentValue.includes( props.value ) )
|
||||
) {
|
||||
boxClassName += ' selected';
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if ( props.type === 'checkbox' ) {
|
||||
let newValue;
|
||||
|
||||
if ( Array.isArray( props.currentValue ) ) {
|
||||
if ( props.currentValue.includes( props.value ) ) {
|
||||
newValue = props.currentValue.filter(
|
||||
( optionValue ) => optionValue !== props.value
|
||||
);
|
||||
} else {
|
||||
newValue = [ ...props.currentValue, props.value ];
|
||||
}
|
||||
} else {
|
||||
newValue = ! props.currentValue;
|
||||
}
|
||||
|
||||
props.changeCallback( newValue );
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ boxClassName }
|
||||
onClick={ props.type === 'checkbox' ? handleClick : undefined }
|
||||
>
|
||||
{ props.type === 'radio' && (
|
||||
<PayPalRdb
|
||||
{ ...{
|
||||
...props,
|
||||
handleRdbState: props.changeCallback,
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
{ props.type === 'checkbox' && <PayPalCheckbox { ...props } /> }
|
||||
<div className="ppcp-r-select-box__content">
|
||||
<div className="ppcp-r-select-box__content-inner">
|
||||
<span className="ppcp-r-select-box__title">
|
||||
{ props.title }
|
||||
</span>
|
||||
<p className="ppcp-r-select-box__description">
|
||||
{ props.description }
|
||||
</p>
|
||||
{ props.children && (
|
||||
<div className="ppcp-r-select-box__additional-content">
|
||||
{ props.children }
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectBox;
|
|
@ -1,5 +0,0 @@
|
|||
const SelectBoxWrapper = ( props ) => {
|
||||
return <div className="ppcp-r-select-box-wrapper">{ props.children }</div>;
|
||||
};
|
||||
|
||||
export default SelectBoxWrapper;
|
|
@ -0,0 +1,47 @@
|
|||
import classNames from 'classnames';
|
||||
import { Description, Header, Title, TitleExtra, Content } from './Elements';
|
||||
|
||||
const SettingsBlock = ( {
|
||||
className,
|
||||
children,
|
||||
title,
|
||||
titleSuffix,
|
||||
description,
|
||||
horizontalLayout = false,
|
||||
separatorAndGap = true,
|
||||
} ) => {
|
||||
const blockClassName = classNames( 'ppcp-r-settings-block', className, {
|
||||
'ppcp--no-gap': ! separatorAndGap,
|
||||
'ppcp--horizontal': horizontalLayout,
|
||||
} );
|
||||
|
||||
const BlockTitle = ( { blockTitle, blockSuffix, blockDescription } ) => {
|
||||
if ( ! blockTitle && ! blockDescription ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Header>
|
||||
<Title>
|
||||
{ blockTitle }
|
||||
<TitleExtra>{ blockSuffix }</TitleExtra>
|
||||
</Title>
|
||||
<Description>{ blockDescription }</Description>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={ blockClassName }>
|
||||
<BlockTitle
|
||||
blockTitle={ title }
|
||||
blockSuffix={ titleSuffix }
|
||||
blockDescription={ description }
|
||||
/>
|
||||
|
||||
<Content asCard={ false }>{ children }</Content>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsBlock;
|
|
@ -1,27 +0,0 @@
|
|||
import Accordion from '../AccordionSection';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
import {
|
||||
Header,
|
||||
Title,
|
||||
Action,
|
||||
Description,
|
||||
TitleWrapper,
|
||||
} from './SettingsBlockElements';
|
||||
|
||||
const SettingsAccordion = ( { title, description, children, ...props } ) => (
|
||||
<SettingsBlock { ...props } className="ppcp-r-settings-block__accordion">
|
||||
<Accordion
|
||||
title={ title }
|
||||
description={ description }
|
||||
Header={ Header }
|
||||
TitleWrapper={ TitleWrapper }
|
||||
Title={ Title }
|
||||
Action={ Action }
|
||||
Description={ Description }
|
||||
>
|
||||
{ children }
|
||||
</Accordion>
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
||||
export default SettingsAccordion;
|
|
@ -1,27 +0,0 @@
|
|||
import { Button } from '@wordpress/components';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
import { Action, Description, Header, Title } from './SettingsBlockElements';
|
||||
|
||||
const ButtonSettingsBlock = ( { title, description, ...props } ) => (
|
||||
<SettingsBlock { ...props } className="ppcp-r-settings-block__button">
|
||||
<Header>
|
||||
<Title>{ title }</Title>
|
||||
<Description>{ description }</Description>
|
||||
</Header>
|
||||
<Action>
|
||||
<Button
|
||||
isBusy={ props.actionProps?.isBusy }
|
||||
variant={ props.actionProps?.buttonType }
|
||||
onClick={
|
||||
props.actionProps?.callback
|
||||
? () => props.actionProps.callback()
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{ props?.actionProps?.value }
|
||||
</Button>
|
||||
</Action>
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
||||
export default ButtonSettingsBlock;
|
|
@ -1,6 +1,7 @@
|
|||
import { Button } from '@wordpress/components';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
import { Header, Title, Action, Description } from './SettingsBlockElements';
|
||||
|
||||
import { Header, Title, Action, Description } from '../Elements';
|
||||
import SettingsBlock from '../SettingsBlock';
|
||||
import TitleBadge from '../TitleBadge';
|
||||
|
||||
const FeatureSettingsBlock = ( { title, description, ...props } ) => {
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
import { TextControl } from '@wordpress/components';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
import {
|
||||
Title,
|
||||
Action,
|
||||
Description,
|
||||
SupplementaryLabel,
|
||||
} from './SettingsBlockElements';
|
||||
|
||||
const DEFAULT_ELEMENT_ORDER = [ 'title', 'action', 'description' ];
|
||||
|
||||
const ELEMENT_RENDERERS = {
|
||||
title: ( { title, supplementaryLabel } ) => (
|
||||
<Title>
|
||||
{ title }
|
||||
{ supplementaryLabel && (
|
||||
<SupplementaryLabel>{ supplementaryLabel }</SupplementaryLabel>
|
||||
) }
|
||||
</Title>
|
||||
),
|
||||
action: ( { actionProps } ) => (
|
||||
<Action>
|
||||
<TextControl
|
||||
className="ppcp-r-vertical-text-control"
|
||||
placeholder={ actionProps?.placeholder }
|
||||
value={ actionProps?.value }
|
||||
onChange={ ( newValue ) =>
|
||||
actionProps?.callback( actionProps?.key, newValue )
|
||||
}
|
||||
/>
|
||||
</Action>
|
||||
),
|
||||
description: ( { description } ) => (
|
||||
<Description>{ description }</Description>
|
||||
),
|
||||
};
|
||||
|
||||
const InputSettingsBlock = ( {
|
||||
title,
|
||||
description,
|
||||
supplementaryLabel,
|
||||
order = DEFAULT_ELEMENT_ORDER,
|
||||
...props
|
||||
} ) => (
|
||||
<SettingsBlock { ...props } className="ppcp-r-settings-block__input">
|
||||
{ order.map( ( elementKey ) => {
|
||||
const RenderElement = ELEMENT_RENDERERS[ elementKey ];
|
||||
return RenderElement ? (
|
||||
<RenderElement
|
||||
key={ elementKey }
|
||||
title={ title }
|
||||
description={ description }
|
||||
supplementaryLabel={ supplementaryLabel }
|
||||
actionProps={ props.actionProps }
|
||||
/>
|
||||
) : null;
|
||||
} ) }
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
||||
export default InputSettingsBlock;
|
|
@ -1,5 +1,6 @@
|
|||
import { ToggleControl } from '@wordpress/components';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
|
||||
import SettingsBlock from '../SettingsBlock';
|
||||
import PaymentMethodIcon from '../PaymentMethodIcon';
|
||||
import data from '../../../utils/data';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import SettingsBlock from './SettingsBlock';
|
||||
import SettingsBlock from '../SettingsBlock';
|
||||
import PaymentMethodItemBlock from './PaymentMethodItemBlock';
|
||||
import { usePaymentMethods } from '../../../data/payment/hooks';
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import SettingsBlock from './SettingsBlock';
|
||||
import { Header, Title, Description } from './SettingsBlockElements';
|
||||
import { PayPalRdbWithContent } from '../Fields';
|
||||
|
||||
const RadioSettingsBlock = ( {
|
||||
title,
|
||||
description,
|
||||
options = [],
|
||||
...props
|
||||
} ) => (
|
||||
<SettingsBlock
|
||||
{ ...props }
|
||||
className="ppcp-r-settings-block__radio ppcp-r-settings-block--expert-rdb"
|
||||
>
|
||||
<Header>
|
||||
<Title>{ title }</Title>
|
||||
<Description>{ description }</Description>
|
||||
</Header>
|
||||
{ options.map( ( option ) => (
|
||||
<PayPalRdbWithContent
|
||||
key={ option.id }
|
||||
id={ option.id }
|
||||
name={ props.actionProps?.name }
|
||||
value={ option.value }
|
||||
currentValue={ props.actionProps?.currentValue }
|
||||
handleRdbState={ ( newValue ) =>
|
||||
props.actionProps?.callback(
|
||||
props.actionProps?.key,
|
||||
newValue
|
||||
)
|
||||
}
|
||||
label={ option.label }
|
||||
description={ option.description }
|
||||
toggleAdditionalContent={ true }
|
||||
>
|
||||
{ option.additionalContent }
|
||||
</PayPalRdbWithContent>
|
||||
) ) }
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
||||
export default RadioSettingsBlock;
|
|
@ -1,53 +0,0 @@
|
|||
import Select, { components } from 'react-select';
|
||||
import data from '../../../utils/data';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
import { Title, Action, Description } from './SettingsBlockElements';
|
||||
|
||||
const DEFAULT_ELEMENT_ORDER = [ 'title', 'action', 'description' ];
|
||||
|
||||
const DropdownIndicator = ( props ) => (
|
||||
<components.DropdownIndicator { ...props }>
|
||||
{ data().getImage( 'icon-arrow-down.svg' ) }
|
||||
</components.DropdownIndicator>
|
||||
);
|
||||
|
||||
const ELEMENT_RENDERERS = {
|
||||
title: ( { title } ) => <Title>{ title }</Title>,
|
||||
action: ( { actionProps } ) => (
|
||||
<Action>
|
||||
<Select
|
||||
className="ppcp-r-multiselect"
|
||||
classNamePrefix="ppcp-r"
|
||||
isMulti={ actionProps?.isMulti }
|
||||
options={ actionProps?.options }
|
||||
components={ { DropdownIndicator } }
|
||||
/>
|
||||
</Action>
|
||||
),
|
||||
description: ( { description } ) => (
|
||||
<Description>{ description }</Description>
|
||||
),
|
||||
};
|
||||
|
||||
const SelectSettingsBlock = ( {
|
||||
title,
|
||||
description,
|
||||
order = DEFAULT_ELEMENT_ORDER,
|
||||
...props
|
||||
} ) => (
|
||||
<SettingsBlock { ...props } className="ppcp-r-settings-block__select">
|
||||
{ order.map( ( elementKey ) => {
|
||||
const RenderElement = ELEMENT_RENDERERS[ elementKey ];
|
||||
return RenderElement ? (
|
||||
<RenderElement
|
||||
key={ elementKey }
|
||||
title={ title }
|
||||
description={ description }
|
||||
actionProps={ props.actionProps }
|
||||
/>
|
||||
) : null;
|
||||
} ) }
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
||||
export default SelectSettingsBlock;
|
|
@ -1,11 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
const SettingsBlock = ( { className, children, separatorAndGap = true } ) => {
|
||||
const blockClassName = classNames( 'ppcp-r-settings-block', className, {
|
||||
'no-gap': ! separatorAndGap,
|
||||
} );
|
||||
|
||||
return <div className={ blockClassName }>{ children }</div>;
|
||||
};
|
||||
|
||||
export default SettingsBlock;
|
|
@ -1,81 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
// Block Elements
|
||||
export const Title = ( {
|
||||
children,
|
||||
altStyle = false,
|
||||
big = false,
|
||||
className = '',
|
||||
} ) => {
|
||||
const elementClasses = classNames(
|
||||
'ppcp-r-settings-block__title',
|
||||
className,
|
||||
{
|
||||
'style-alt': altStyle,
|
||||
'style-big': big,
|
||||
}
|
||||
);
|
||||
|
||||
return <span className={ elementClasses }>{ children }</span>;
|
||||
};
|
||||
|
||||
export const TitleWrapper = ( { children } ) => (
|
||||
<span className="ppcp-r-settings-block__title-wrapper">{ children }</span>
|
||||
);
|
||||
|
||||
export const SupplementaryLabel = ( { children } ) => (
|
||||
<span className="ppcp-r-settings-block__supplementary-title-label">
|
||||
{ children }
|
||||
</span>
|
||||
);
|
||||
|
||||
export const Description = ( { children, asHtml = false, className = '' } ) => {
|
||||
// Don't output anything if description is empty.
|
||||
if ( ! children ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const elementClasses = classNames(
|
||||
'ppcp-r-settings-block__description',
|
||||
className
|
||||
);
|
||||
|
||||
if ( ! asHtml ) {
|
||||
return <span className={ elementClasses }>{ children }</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
className={ elementClasses }
|
||||
dangerouslySetInnerHTML={ { __html: children } }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Action = ( { children } ) => (
|
||||
<div className="ppcp-r-settings-block__action">{ children }</div>
|
||||
);
|
||||
|
||||
export const Header = ( { children, className = '' } ) => (
|
||||
<div className={ `ppcp-r-settings-block__header ${ className }`.trim() }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
|
||||
// Card Elements
|
||||
export const Content = ( { children, className = '', id = '' } ) => {
|
||||
const elementClasses = classNames(
|
||||
'ppcp-r-settings-card__content',
|
||||
className
|
||||
);
|
||||
|
||||
return (
|
||||
<div id={ id } className={ elementClasses }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ContentWrapper = ( { children } ) => (
|
||||
<div className="ppcp-r-settings-card__content-wrapper">{ children }</div>
|
||||
);
|
|
@ -1,27 +0,0 @@
|
|||
import { ToggleControl } from '@wordpress/components';
|
||||
import SettingsBlock from './SettingsBlock';
|
||||
import { Header, Title, Action, Description } from './SettingsBlockElements';
|
||||
|
||||
const ToggleSettingsBlock = ( { title, description, ...props } ) => (
|
||||
<SettingsBlock { ...props } className="ppcp-r-settings-block__toggle">
|
||||
<Action>
|
||||
<ToggleControl
|
||||
className="ppcp-r-settings-block__toggle-control"
|
||||
__nextHasNoMarginBottom={ true }
|
||||
checked={ props.actionProps?.value }
|
||||
onChange={ ( newValue ) =>
|
||||
props.actionProps?.callback(
|
||||
props.actionProps?.key,
|
||||
newValue
|
||||
)
|
||||
}
|
||||
/>
|
||||
</Action>
|
||||
<Header>
|
||||
{ title && <Title>{ title }</Title> }
|
||||
{ description && <Description>{ description }</Description> }
|
||||
</Header>
|
||||
</SettingsBlock>
|
||||
);
|
||||
|
||||
export default ToggleSettingsBlock;
|
|
@ -1,20 +1,4 @@
|
|||
export { default as SettingsBlock } from './SettingsBlock';
|
||||
export { default as ButtonSettingsBlock } from './ButtonSettingsBlock';
|
||||
export { default as InputSettingsBlock } from './InputSettingsBlock';
|
||||
export { default as SelectSettingsBlock } from './SelectSettingsBlock';
|
||||
export { default as AccordionSettingsBlock } from './AccordionSettingsBlock';
|
||||
export { default as ToggleSettingsBlock } from './ToggleSettingsBlock';
|
||||
export { default as RadioSettingsBlock } from './RadioSettingsBlock';
|
||||
export { default as PaymentMethodsBlock } from './PaymentMethodsBlock';
|
||||
export { default as PaymentMethodItemBlock } from './PaymentMethodItemBlock';
|
||||
|
||||
export {
|
||||
Title,
|
||||
TitleWrapper,
|
||||
SupplementaryLabel,
|
||||
Description,
|
||||
Action,
|
||||
Content,
|
||||
ContentWrapper,
|
||||
Header,
|
||||
} from './SettingsBlockElements';
|
||||
export { default as TodoSettingsBlock } from './TodoSettingsBlock';
|
||||
export { default as FeatureSettingsBlock } from './FeatureSettingsBlock';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { Content, ContentWrapper } from './SettingsBlocks';
|
||||
import { Content, ContentWrapper } from './Elements';
|
||||
|
||||
const SettingsCard = ( {
|
||||
id,
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { Spinner } from '@wordpress/components';
|
||||
|
||||
const SpinnerOverlay = ( { message = '' } ) => {
|
||||
const SpinnerOverlay = ( { message = null } ) => {
|
||||
if ( null === message ) {
|
||||
message = __( 'Loading…', 'woocommerce-paypal-payments' );
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-spinner-overlay">
|
||||
{ message && (
|
||||
|
|
|
@ -20,10 +20,10 @@ const TopNavigation = ( {
|
|||
const { isScrolled } = useIsScrolled();
|
||||
|
||||
const className = classNames( 'ppcp-r-navigation-container', {
|
||||
'is-scrolled': isScrolled,
|
||||
'ppcp--is-scrolled': isScrolled,
|
||||
} );
|
||||
const titleClassName = classNames( 'title', {
|
||||
big: isMainTitle,
|
||||
const titleClassName = classNames( 'ppcp--nav-title', {
|
||||
'ppcp--big': isMainTitle,
|
||||
} );
|
||||
|
||||
const handleTitleClick = useCallback( () => {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { __, sprintf } from '@wordpress/i18n';
|
|||
import BadgeBox, {
|
||||
BADGE_BOX_TITLE_BIG,
|
||||
} from '../../../ReusableComponents/BadgeBox';
|
||||
import Separator from '../../../ReusableComponents/Separator';
|
||||
import { Separator } from '../../../ReusableComponents/Elements';
|
||||
import PricingTitleBadge from '../../../ReusableComponents/PricingTitleBadge';
|
||||
import OptionalPaymentMethods from './OptionalPaymentMethods';
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
import BadgeBox from '../../../ReusableComponents/BadgeBox';
|
||||
import Separator from '../../../ReusableComponents/Separator';
|
||||
import { Separator } from '../../../ReusableComponents/Elements';
|
||||
import PricingTitleBadge from '../../../ReusableComponents/PricingTitleBadge';
|
||||
|
||||
const AcdcOptionalPaymentMethods = ( {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Separator from '../../../ReusableComponents/Separator';
|
||||
import { Separator } from '../../../ReusableComponents/Elements';
|
||||
import SandboxConnectionForm from './SandboxConnectionForm';
|
||||
import ManualConnectionForm from './ManualConnectionForm';
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { __, sprintf } from '@wordpress/i18n';
|
|||
import BadgeBox, {
|
||||
BADGE_BOX_TITLE_BIG,
|
||||
} from '../../../ReusableComponents/BadgeBox';
|
||||
import Separator from '../../../ReusableComponents/Separator';
|
||||
import { Separator } from '../../../ReusableComponents/Elements';
|
||||
import PricingTitleBadge from '../../../ReusableComponents/PricingTitleBadge';
|
||||
import OptionalPaymentMethods from './OptionalPaymentMethods';
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ const ConnectionButton = ( {
|
|||
removeCompleteHandler,
|
||||
} = useHandleOnboardingButton( isSandbox );
|
||||
const buttonClassName = classNames( 'ppcp-r-connection-button', className, {
|
||||
'sandbox-mode': isSandbox,
|
||||
'live-mode': ! isSandbox,
|
||||
'ppcp--mode-sandbox': isSandbox,
|
||||
'ppcp--mode-live': ! isSandbox,
|
||||
} );
|
||||
const environment = isSandbox ? 'sandbox' : 'production';
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ const ManualConnectionForm = () => {
|
|||
value={ manualClientId }
|
||||
onChange={ setManualClientId }
|
||||
className={ classNames( {
|
||||
'has-error': ! clientValid,
|
||||
'ppcp--has-error': ! clientValid,
|
||||
} ) }
|
||||
/>
|
||||
{ clientValid || (
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
|
||||
import SelectBoxWrapper from '../../../ReusableComponents/SelectBoxWrapper';
|
||||
import SelectBox from '../../../ReusableComponents/SelectBox';
|
||||
import { OptionSelector } from '../../../ReusableComponents/Fields';
|
||||
import { OnboardingHooks, BUSINESS_TYPES } from '../../../../data';
|
||||
import OnboardingHeader from '../Components/OnboardingHeader';
|
||||
|
||||
const BUSINESS_RADIO_GROUP_NAME = 'business';
|
||||
const getBusinessType = ( isCasualSeller ) => {
|
||||
if ( isCasualSeller === null ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return isCasualSeller
|
||||
? BUSINESS_TYPES.CASUAL_SELLER
|
||||
: BUSINESS_TYPES.BUSINESS;
|
||||
};
|
||||
|
||||
const StepBusiness = ( {} ) => {
|
||||
const { isCasualSeller, setIsCasualSeller } = OnboardingHooks.useBusiness();
|
||||
const [ businessChoice, setBusinessChoice ] = useState(
|
||||
getBusinessType( isCasualSeller )
|
||||
);
|
||||
|
||||
const handleSellerTypeChange = ( value ) =>
|
||||
setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === value );
|
||||
|
||||
const getCurrentValue = () => {
|
||||
if ( isCasualSeller === null ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return isCasualSeller
|
||||
? BUSINESS_TYPES.CASUAL_SELLER
|
||||
: BUSINESS_TYPES.BUSINESS;
|
||||
};
|
||||
useEffect( () => {
|
||||
setIsCasualSeller( BUSINESS_TYPES.CASUAL_SELLER === businessChoice );
|
||||
}, [ businessChoice, setIsCasualSeller ] );
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-page-business">
|
||||
|
@ -32,43 +34,34 @@ const StepBusiness = ( {} ) => {
|
|||
) }
|
||||
/>
|
||||
<div className="ppcp-r-inner-container">
|
||||
<SelectBoxWrapper>
|
||||
<SelectBox
|
||||
title={ __(
|
||||
'Business',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Recommended for individuals and organizations that primarily use PayPal to sell goods or services or receive donations, even if your business is not incorporated.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
name={ BUSINESS_RADIO_GROUP_NAME }
|
||||
value={ BUSINESS_TYPES.BUSINESS }
|
||||
changeCallback={ handleSellerTypeChange }
|
||||
currentValue={ getCurrentValue() }
|
||||
checked={ isCasualSeller === false }
|
||||
type="radio"
|
||||
></SelectBox>
|
||||
<SelectBox
|
||||
title={ __(
|
||||
'Personal Account',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Ideal for those who primarily make purchases or send personal transactions to family and friends.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
name={ BUSINESS_RADIO_GROUP_NAME }
|
||||
value={ BUSINESS_TYPES.CASUAL_SELLER }
|
||||
changeCallback={ handleSellerTypeChange }
|
||||
currentValue={ getCurrentValue() }
|
||||
checked={ isCasualSeller === true }
|
||||
type="radio"
|
||||
></SelectBox>
|
||||
</SelectBoxWrapper>
|
||||
<OptionSelector
|
||||
multiSelect={ false }
|
||||
options={ businessChoices }
|
||||
onChange={ setBusinessChoice }
|
||||
value={ businessChoice }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const businessChoices = [
|
||||
{
|
||||
value: BUSINESS_TYPES.BUSINESS,
|
||||
title: __( 'Business', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Recommended for individuals and organizations that primarily use PayPal to sell goods or services or receive donations, even if your business is not incorporated.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
value: BUSINESS_TYPES.CASUAL_SELLER,
|
||||
title: __( 'Personal Account', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Ideal for those who primarily make purchases or send personal transactions to family and friends.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export default StepBusiness;
|
||||
|
|
|
@ -1,71 +1,26 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import { CommonHooks, OnboardingHooks } from '../../../../data';
|
||||
import SelectBoxWrapper from '../../../ReusableComponents/SelectBoxWrapper';
|
||||
import SelectBox from '../../../ReusableComponents/SelectBox';
|
||||
import { OptionSelector } from '../../../ReusableComponents/Fields';
|
||||
import PricingDescription from '../../../ReusableComponents/PricingDescription';
|
||||
import OnboardingHeader from '../Components/OnboardingHeader';
|
||||
import OptionalPaymentMethods from '../Components/OptionalPaymentMethods';
|
||||
|
||||
const OPM_RADIO_GROUP_NAME = 'optional-payment-methods';
|
||||
|
||||
const StepPaymentMethods = ( {} ) => {
|
||||
const {
|
||||
areOptionalPaymentMethodsEnabled,
|
||||
setAreOptionalPaymentMethodsEnabled,
|
||||
} = OnboardingHooks.useOptionalPaymentMethods();
|
||||
|
||||
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
|
||||
|
||||
let screenTitle = __(
|
||||
'Add optional payment methods to your Checkout',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
|
||||
if ( 'US' === storeCountry ) {
|
||||
screenTitle = __(
|
||||
'Add Expanded Checkout for More Ways to Pay',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
}
|
||||
const { optionalMethods, setOptionalMethods } =
|
||||
OnboardingHooks.useOptionalPaymentMethods();
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-page-optional-payment-methods">
|
||||
<OnboardingHeader title={ screenTitle } />
|
||||
<OnboardingHeader title={ <PaymentStepTitle /> } />
|
||||
<div className="ppcp-r-inner-container">
|
||||
<SelectBoxWrapper>
|
||||
<SelectBox
|
||||
title={ __(
|
||||
'Available with additional application',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={
|
||||
<OptionalPaymentMethods
|
||||
useAcdc={ true }
|
||||
isFastlane={ true }
|
||||
isPayLater={ true }
|
||||
storeCountry={ storeCountry }
|
||||
storeCurrency={ storeCurrency }
|
||||
/>
|
||||
}
|
||||
name={ OPM_RADIO_GROUP_NAME }
|
||||
value={ true }
|
||||
changeCallback={ setAreOptionalPaymentMethodsEnabled }
|
||||
currentValue={ areOptionalPaymentMethodsEnabled }
|
||||
type="radio"
|
||||
></SelectBox>
|
||||
<SelectBox
|
||||
title={ __(
|
||||
'No thanks, I prefer to use a different provider for processing credit cards, digital wallets, and local payment methods',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
name={ OPM_RADIO_GROUP_NAME }
|
||||
value={ false }
|
||||
changeCallback={ setAreOptionalPaymentMethodsEnabled }
|
||||
currentValue={ areOptionalPaymentMethodsEnabled }
|
||||
type="radio"
|
||||
></SelectBox>
|
||||
</SelectBoxWrapper>
|
||||
<OptionSelector
|
||||
multiSelect={ false }
|
||||
options={ methodChoices }
|
||||
onChange={ setOptionalMethods }
|
||||
value={ optionalMethods }
|
||||
/>
|
||||
|
||||
<PricingDescription />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,3 +28,51 @@ const StepPaymentMethods = ( {} ) => {
|
|||
};
|
||||
|
||||
export default StepPaymentMethods;
|
||||
|
||||
const PaymentStepTitle = () => {
|
||||
const { storeCountry } = CommonHooks.useWooSettings();
|
||||
|
||||
if ( 'US' === storeCountry ) {
|
||||
return __(
|
||||
'Add Expanded Checkout for More Ways to Pay',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
}
|
||||
|
||||
return __(
|
||||
'Add optional payment methods to your Checkout',
|
||||
'woocommerce-paypal-payments'
|
||||
);
|
||||
};
|
||||
|
||||
const OptionalMethodDescription = () => {
|
||||
const { storeCountry, storeCurrency } = CommonHooks.useWooSettings();
|
||||
|
||||
return (
|
||||
<OptionalPaymentMethods
|
||||
useAcdc={ true }
|
||||
isFastlane={ true }
|
||||
isPayLater={ true }
|
||||
storeCountry={ storeCountry }
|
||||
storeCurrency={ storeCurrency }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const methodChoices = [
|
||||
{
|
||||
value: true,
|
||||
title: __(
|
||||
'Available with additional application',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
description: <OptionalMethodDescription />,
|
||||
},
|
||||
{
|
||||
title: __(
|
||||
'No thanks, I prefer to use a different provider for processing credit cards, digital wallets, and local payment methods',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
value: false,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,15 +1,53 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
|
||||
import SelectBox from '../../../ReusableComponents/SelectBox';
|
||||
import SelectBoxWrapper from '../../../ReusableComponents/SelectBoxWrapper';
|
||||
import { OptionSelector } from '../../../ReusableComponents/Fields';
|
||||
import { OnboardingHooks, PRODUCT_TYPES } from '../../../../data';
|
||||
import OnboardingHeader from '../Components/OnboardingHeader';
|
||||
|
||||
const PRODUCTS_CHECKBOX_GROUP_NAME = 'products';
|
||||
|
||||
const StepProducts = () => {
|
||||
const { products, setProducts } = OnboardingHooks.useProducts();
|
||||
const { canUseSubscriptions } = OnboardingHooks.useFlags();
|
||||
const [ optionState, setOptionState ] = useState( null );
|
||||
const [ productChoices, setProductChoices ] = useState( [] );
|
||||
|
||||
useEffect( () => {
|
||||
const initChoices = () => {
|
||||
if ( optionState === canUseSubscriptions ) {
|
||||
return;
|
||||
}
|
||||
|
||||
let choices = productChoicesFull;
|
||||
|
||||
// Remove subscription details, if not available.
|
||||
if ( ! canUseSubscriptions ) {
|
||||
choices = choices.filter(
|
||||
( { value } ) => value !== PRODUCT_TYPES.SUBSCRIPTIONS
|
||||
);
|
||||
setProducts(
|
||||
products.filter(
|
||||
( value ) => value !== PRODUCT_TYPES.SUBSCRIPTIONS
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
setProductChoices( choices );
|
||||
setOptionState( canUseSubscriptions );
|
||||
};
|
||||
|
||||
initChoices();
|
||||
}, [ canUseSubscriptions, optionState, products, setProducts ] );
|
||||
|
||||
const handleChange = ( key, checked ) => {
|
||||
const getNewValue = () => {
|
||||
if ( checked ) {
|
||||
return [ ...products, key ];
|
||||
}
|
||||
return products.filter( ( val ) => val !== key );
|
||||
};
|
||||
|
||||
setProducts( getNewValue() );
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ppcp-r-page-products">
|
||||
|
@ -20,104 +58,70 @@ const StepProducts = () => {
|
|||
) }
|
||||
/>
|
||||
<div className="ppcp-r-inner-container">
|
||||
<SelectBoxWrapper>
|
||||
<SelectBox
|
||||
title={ __( 'Virtual', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Items do not require shipping.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
||||
value={ PRODUCT_TYPES.VIRTUAL }
|
||||
changeCallback={ setProducts }
|
||||
currentValue={ products }
|
||||
type="checkbox"
|
||||
>
|
||||
<ul className="ppcp-r-services">
|
||||
<li>
|
||||
{ __(
|
||||
'Services',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</li>
|
||||
<li>
|
||||
{ __(
|
||||
'Downloadable',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</li>
|
||||
<li>
|
||||
{ __(
|
||||
'Bookings',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</li>
|
||||
<li>
|
||||
{ __(
|
||||
'Deposits',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</li>
|
||||
</ul>
|
||||
</SelectBox>
|
||||
<SelectBox
|
||||
title={ __(
|
||||
'Physical Goods',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Items require shipping.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
||||
value={ PRODUCT_TYPES.PHYSICAL }
|
||||
changeCallback={ setProducts }
|
||||
currentValue={ products }
|
||||
type="checkbox"
|
||||
>
|
||||
<ul className="ppcp-r-services">
|
||||
<li>
|
||||
{ __( 'Goods', 'woocommerce-paypal-payments' ) }
|
||||
</li>
|
||||
<li>
|
||||
{ __(
|
||||
'Deliveries',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</li>
|
||||
</ul>
|
||||
</SelectBox>
|
||||
{ canUseSubscriptions && (
|
||||
<SelectBox
|
||||
title={ __(
|
||||
'Subscriptions',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Recurring payments for either physical goods or services.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
name={ PRODUCTS_CHECKBOX_GROUP_NAME }
|
||||
value={ PRODUCT_TYPES.SUBSCRIPTIONS }
|
||||
changeCallback={ setProducts }
|
||||
currentValue={ products }
|
||||
type="checkbox"
|
||||
>
|
||||
<a
|
||||
target="__blank"
|
||||
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
|
||||
>
|
||||
{ __(
|
||||
'WooCommerce Subscriptions',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</a>
|
||||
</SelectBox>
|
||||
) }
|
||||
</SelectBoxWrapper>
|
||||
<OptionSelector
|
||||
multiSelect={ true }
|
||||
options={ productChoices }
|
||||
onChange={ handleChange }
|
||||
value={ products }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepProducts;
|
||||
|
||||
const DetailsVirtual = () => (
|
||||
<ul className="ppcp-r-services">
|
||||
<li>{ __( 'Services', 'woocommerce-paypal-payments' ) }</li>
|
||||
<li>{ __( 'Downloadable', 'woocommerce-paypal-payments' ) }</li>
|
||||
<li>{ __( 'Bookings', 'woocommerce-paypal-payments' ) }</li>
|
||||
<li>{ __( 'Deposits', 'woocommerce-paypal-payments' ) }</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
const DetailsPhysical = () => (
|
||||
<ul className="ppcp-r-services">
|
||||
<li>{ __( 'Goods', 'woocommerce-paypal-payments' ) }</li>
|
||||
<li>{ __( 'Deliveries', 'woocommerce-paypal-payments' ) }</li>
|
||||
</ul>
|
||||
);
|
||||
|
||||
const DetailsSubscriptions = () => (
|
||||
<a
|
||||
target="__blank"
|
||||
href="https://woocommerce.com/document/woocommerce-paypal-payments/#subscriptions-faq"
|
||||
>
|
||||
{ __( 'WooCommerce Subscriptions', 'woocommerce-paypal-payments' ) }
|
||||
</a>
|
||||
);
|
||||
|
||||
const productChoicesFull = [
|
||||
{
|
||||
value: PRODUCT_TYPES.VIRTUAL,
|
||||
title: __( 'Virtual', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Items do not require shipping.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
contents: <DetailsVirtual />,
|
||||
},
|
||||
{
|
||||
value: PRODUCT_TYPES.PHYSICAL,
|
||||
title: __( 'Physical Goods', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Items require shipping.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
contents: <DetailsPhysical />,
|
||||
},
|
||||
{
|
||||
value: PRODUCT_TYPES.SUBSCRIPTIONS,
|
||||
title: __( 'Subscriptions', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Recurring payments for either physical goods or services.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
contents: <DetailsSubscriptions />,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -2,8 +2,8 @@ import { __ } from '@wordpress/i18n';
|
|||
import { Button } from '@wordpress/components';
|
||||
|
||||
import PaymentMethodIcons from '../../../ReusableComponents/PaymentMethodIcons';
|
||||
import Separator from '../../../ReusableComponents/Separator';
|
||||
import AccordionSection from '../../../ReusableComponents/AccordionSection';
|
||||
import { Separator } from '../../../ReusableComponents/Elements';
|
||||
import Accordion from '../../../ReusableComponents/AccordionSection';
|
||||
import { CommonHooks } from '../../../../data';
|
||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||
import OnboardingHeader from '../Components/OnboardingHeader';
|
||||
|
@ -55,16 +55,17 @@ const StepWelcome = ( { setStep, currentStep } ) => {
|
|||
storeCountry={ storeCountry }
|
||||
/>
|
||||
<Separator text={ __( 'or', 'woocommerce-paypal-payments' ) } />
|
||||
<AccordionSection
|
||||
<Accordion
|
||||
title={ __(
|
||||
'See advanced options',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
className="onboarding-advanced-options"
|
||||
noCaps={ true }
|
||||
id="advanced-options"
|
||||
>
|
||||
<AdvancedOptionsForm />
|
||||
</AccordionSection>
|
||||
</Accordion>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import SettingsCard from '../../ReusableComponents/SettingsCard';
|
||||
import PaymentMethodsBlock from '../../ReusableComponents/SettingsBlocks/PaymentMethodsBlock';
|
||||
import { PaymentMethodsBlock } from '../../ReusableComponents/SettingsBlocks';
|
||||
import { PaymentHooks } from '../../../data';
|
||||
import { useActiveModal } from '../../../data/common/hooks';
|
||||
import Modal from './TabSettingsElements/Blocks/Modal';
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import ConnectionStatus from './TabSettingsElements/ConnectionStatus';
|
||||
import CommonSettings from './TabSettingsElements/CommonSettings';
|
||||
import ExpertSettings from './TabSettingsElements/ExpertSettings';
|
||||
import { useSettings } from '../../../data/settings-tab/hooks';
|
||||
|
||||
const TabSettings = () => {
|
||||
const { settings, setSettings } = useSettings();
|
||||
|
||||
const updateFormValue = ( key, value ) => {
|
||||
setSettings( {
|
||||
...settings,
|
||||
[ key ]: value,
|
||||
} );
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="ppcp-r-settings">
|
||||
<ConnectionStatus />
|
||||
<CommonSettings
|
||||
settings={ settings }
|
||||
updateFormValue={ updateFormValue }
|
||||
/>
|
||||
<ExpertSettings
|
||||
settings={ settings }
|
||||
updateFormValue={ updateFormValue }
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TabSettings;
|
|
@ -1,14 +1,12 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
import {
|
||||
AccordionSettingsBlock,
|
||||
RadioSettingsBlock,
|
||||
InputSettingsBlock,
|
||||
} from '../../../../ReusableComponents/SettingsBlocks';
|
||||
import {
|
||||
sandboxData,
|
||||
productionData,
|
||||
} from '../../../../../data/settings/connection-details-data';
|
||||
ControlTextInput,
|
||||
ControlRadioGroup,
|
||||
} from '../../../../ReusableComponents/Controls';
|
||||
import Accordion from '../../../../ReusableComponents/AccordionSection';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
|
||||
const ConnectionDetails = ( { settings, updateFormValue } ) => {
|
||||
const isSandbox = settings.sandboxConnected;
|
||||
|
@ -20,22 +18,192 @@ const ConnectionDetails = ( { settings, updateFormValue } ) => {
|
|||
const modeKey = isSandbox ? 'productionMode' : 'sandboxMode';
|
||||
|
||||
return (
|
||||
<AccordionSettingsBlock
|
||||
<Accordion
|
||||
title={ modeConfig.title }
|
||||
description={ modeConfig.description }
|
||||
>
|
||||
<RadioSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ modeConfig.connectTitle }
|
||||
description={ modeConfig.connectDescription }
|
||||
options={ modeConfig.options }
|
||||
actionProps={ {
|
||||
key: modeKey,
|
||||
currentValue: settings[ modeKey ],
|
||||
callback: updateFormValue,
|
||||
} }
|
||||
/>
|
||||
</AccordionSettingsBlock>
|
||||
>
|
||||
<ControlRadioGroup
|
||||
options={ modeConfig.options }
|
||||
value={ settings[ modeKey ] }
|
||||
onChange={ updateFormValue }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectionDetails;
|
||||
|
||||
// Helper logic, refactor this when possible.
|
||||
|
||||
/**
|
||||
* Generates options for the environment mode settings.
|
||||
*
|
||||
* @param {Object} config - Configuration for the mode.
|
||||
* @param {Object} settings - Current settings.
|
||||
* @param {Function} updateFormValue - Callback to update settings.
|
||||
* @return {Array} Options array.
|
||||
*/
|
||||
const generateOptions = ( config, settings, updateFormValue ) => [
|
||||
{
|
||||
id: `${ config.mode }_mode`,
|
||||
value: `${ config.mode }_mode`,
|
||||
label: config.labelTitle,
|
||||
description: config.labelDescription,
|
||||
additionalContent: (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={ () => {
|
||||
updateFormValue( `${ config.mode }Connected`, true );
|
||||
if ( config.mode === 'production' ) {
|
||||
global.ppcpSettings.startOnboarding();
|
||||
}
|
||||
} }
|
||||
>
|
||||
{ config.buttonText }
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'manual_connect',
|
||||
value: 'manual_connect',
|
||||
label: __( 'Manual Connect', 'woocommerce-paypal-payments' ),
|
||||
description: sprintf(
|
||||
__(
|
||||
'For advanced users: Connect a custom PayPal REST app for full control over your integration. For more information on creating a PayPal REST application, <a target="_blank" href="%s">click here</a>.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'#'
|
||||
),
|
||||
additionalContent: (
|
||||
<>
|
||||
<ControlTextInput
|
||||
title={ config.clientIdTitle }
|
||||
// Input field props.
|
||||
value={ settings[ `${ config.mode }ClientId` ] }
|
||||
onChange={ updateFormValue }
|
||||
placeholder={ __(
|
||||
'Enter Client ID',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
/>
|
||||
<ControlTextInput
|
||||
title={ config.secretKeyTitle }
|
||||
// Input field props.
|
||||
value={ settings[ `${ config.mode }SecretKey` ] }
|
||||
onChange={ updateFormValue }
|
||||
placeholder={ __(
|
||||
'Enter Secret Key',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
/>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={ () =>
|
||||
updateFormValue(
|
||||
`${ config.mode }ManuallyConnected`,
|
||||
true
|
||||
)
|
||||
}
|
||||
>
|
||||
{ __( 'Connect Account', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Generates data for a given mode (sandbox or production).
|
||||
*
|
||||
* @param {Object} config - Configuration for the mode.
|
||||
* @param {Object} settings - Current settings.
|
||||
* @param {Function} updateFormValue - Callback to update settings.
|
||||
* @return {Object} Mode configuration.
|
||||
*/
|
||||
const generateModeData = ( config, settings, updateFormValue ) => ( {
|
||||
title: config.title,
|
||||
description: config.description,
|
||||
connectTitle: __(
|
||||
`Connect ${ config.label } Account`, // TODO: Avoid variables inside __() translation literal.
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
connectDescription: config.connectDescription,
|
||||
options: generateOptions( config, settings, updateFormValue ),
|
||||
} );
|
||||
|
||||
const sandboxData = ( { settings = {}, updateFormValue = () => {} } ) =>
|
||||
generateModeData(
|
||||
{
|
||||
mode: 'sandbox',
|
||||
label: 'Sandbox',
|
||||
title: __( 'Sandbox', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
"Test your site in PayPal's Sandbox environment.",
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
connectDescription: __(
|
||||
'Connect a PayPal Sandbox account in order to test your website. Transactions made will not result in actual money movement. Do not fulfil orders completed in Sandbox mode.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
labelTitle: __( 'Sandbox Mode', 'woocommerce-paypal-payments' ),
|
||||
labelDescription: __(
|
||||
'Activate Sandbox mode to safely test PayPal with sample data. Once your store is ready to go live, you can easily switch to your production account.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
buttonText: __(
|
||||
'Connect Sandbox Account',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
clientIdTitle: __(
|
||||
'Sandbox Client ID',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
secretKeyTitle: __(
|
||||
'Sandbox Secret Key',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
settings,
|
||||
updateFormValue
|
||||
);
|
||||
|
||||
const productionData = ( { settings = {}, updateFormValue = () => {} } ) =>
|
||||
generateModeData(
|
||||
{
|
||||
mode: 'production',
|
||||
label: 'Live',
|
||||
title: __( 'Live Payments', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Your site is currently configured in Sandbox mode to test payments. When you are ready, launch your site and receive live payments via PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
connectDescription: __(
|
||||
'Connect a live PayPal account to launch your site and receive live payments via PayPal. PayPal will guide you through the setup process.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
labelTitle: __( 'Production Mode', 'woocommerce-paypal-payments' ),
|
||||
labelDescription: __(
|
||||
'Activate Production mode to connect your live account and receive live payments via PayPal. Stay connected in Sandbox mode to continue testing payments before going live.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
buttonText: __(
|
||||
'Set up and connect live PayPal Account',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
clientIdTitle: __(
|
||||
'Live Account Client ID',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
secretKeyTitle: __(
|
||||
'Live Account Secret Key',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
settings,
|
||||
updateFormValue
|
||||
);
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
Header,
|
||||
SettingsBlock,
|
||||
Title,
|
||||
Description,
|
||||
ToggleSettingsBlock,
|
||||
} from '../../../../ReusableComponents/SettingsBlocks';
|
||||
|
||||
const OrderIntent = ( { updateFormValue, settings } ) => {
|
||||
return (
|
||||
<SettingsBlock>
|
||||
<Header>
|
||||
<Title>
|
||||
{ __( 'Order Intent', 'woocommerce-paypal-payments' ) }
|
||||
</Title>
|
||||
<Description>
|
||||
{ __(
|
||||
'Choose between immediate capture or authorization-only, with manual capture in the Order section.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Description>
|
||||
</Header>
|
||||
|
||||
<ToggleSettingsBlock
|
||||
title={ __( 'Authorize Only', 'woocommerce-paypal-payments' ) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'authorizeOnly',
|
||||
value: settings.authorizeOnly,
|
||||
} }
|
||||
/>
|
||||
|
||||
<ToggleSettingsBlock
|
||||
title={ __(
|
||||
'Capture Virtual-Only Orders',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'captureVirtualOnlyOrders',
|
||||
value: settings.captureVirtualOnlyOrders,
|
||||
} }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderIntent;
|
|
@ -1,10 +1,48 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
AccordionSettingsBlock,
|
||||
SelectSettingsBlock,
|
||||
} from '../../../../ReusableComponents/SettingsBlocks';
|
||||
|
||||
const creditCardExamples = [
|
||||
import Accordion from '../../../../ReusableComponents/AccordionSection';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
import { ControlSelect } from '../../../../ReusableComponents/Controls';
|
||||
import { SettingsHooks } from '../../../../../data';
|
||||
|
||||
const OtherSettings = () => {
|
||||
const { disabledCards, setDisabledCards } = SettingsHooks.useSettings();
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
title={ __(
|
||||
'Other payment method settings',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Modify the checkout experience for alternative payment methods, credit cards, and digital wallets.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<SettingsBlock
|
||||
title={ __(
|
||||
'Disable specific credit cards',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
"If left blank, PayPal and other buttons will present in the user's detected language. Enter a language here to force all buttons to display in that language.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<ControlSelect
|
||||
options={ disabledCardChoices }
|
||||
value={ disabledCards }
|
||||
onChange={ setDisabledCards }
|
||||
isMulti={ true }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default OtherSettings;
|
||||
|
||||
const disabledCardChoices = [
|
||||
{ value: '', label: __( 'Select', 'woocommerce-paypal-payments' ) },
|
||||
{
|
||||
value: 'mastercard',
|
||||
|
@ -21,39 +59,3 @@ const creditCardExamples = [
|
|||
label: __( 'Diners Club', 'woocommerce-paypal-payments' ),
|
||||
},
|
||||
];
|
||||
|
||||
const OtherSettings = ( { settings, updateFormValue } ) => {
|
||||
return (
|
||||
<AccordionSettingsBlock
|
||||
title={ __(
|
||||
'Other payment method settings',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Modify the checkout experience for alternative payment methods, credit cards, and digital wallets.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<SelectSettingsBlock
|
||||
title={ __(
|
||||
'Disable specific credit cards',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
"If left blank, PayPal and other buttons will present in the user's detected language. Enter a language here to force all buttons to display in that language.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
options: creditCardExamples,
|
||||
value: settings.buttonLanguage,
|
||||
callback: updateFormValue,
|
||||
key: 'buttonLanguage',
|
||||
isMulti: true,
|
||||
} }
|
||||
order={ [ 'title', 'description', 'action' ] }
|
||||
/>
|
||||
</AccordionSettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default OtherSettings;
|
||||
|
|
|
@ -1,28 +1,41 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
AccordionSettingsBlock,
|
||||
RadioSettingsBlock,
|
||||
ToggleSettingsBlock,
|
||||
InputSettingsBlock,
|
||||
SelectSettingsBlock,
|
||||
} from '../../../../ReusableComponents/SettingsBlocks';
|
||||
|
||||
const PaypalSettings = ( { updateFormValue, settings } ) => {
|
||||
import {
|
||||
ControlRadioGroup,
|
||||
ControlToggleButton,
|
||||
ControlTextInput,
|
||||
ControlSelect,
|
||||
} from '../../../../ReusableComponents/Controls';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
import Accordion from '../../../../ReusableComponents/AccordionSection';
|
||||
import { SettingsHooks } from '../../../../../data';
|
||||
|
||||
const PaypalSettings = () => {
|
||||
const {
|
||||
savePaypalAndVenmo,
|
||||
setSavePaypalAndVenmo,
|
||||
subtotalAdjustment,
|
||||
setSubtotalAdjustment,
|
||||
brandName,
|
||||
setBrandName,
|
||||
softDescriptor,
|
||||
setSoftDescriptor,
|
||||
landingPage,
|
||||
setLandingPage,
|
||||
buttonLanguage,
|
||||
setButtonLanguage,
|
||||
} = SettingsHooks.useSettings();
|
||||
|
||||
return (
|
||||
<AccordionSettingsBlock
|
||||
className="ppcp-r-settings-block--settings"
|
||||
<Accordion
|
||||
className="ppcp--paypal-settings"
|
||||
title={ __( 'PayPal Settings', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Modify the PayPal checkout experience.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'payNowExperience',
|
||||
value: settings.payNowExperience,
|
||||
} }
|
||||
>
|
||||
<RadioSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __(
|
||||
'Subtotal mismatch fallback',
|
||||
'woocommerce-paypal-payments'
|
||||
|
@ -31,93 +44,64 @@ const PaypalSettings = ( { updateFormValue, settings } ) => {
|
|||
'Due to differences in how WooCommerce and PayPal calculates taxes, some transactions may fail due to a rounding error. This settings determines the fallback behavior.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
options={ [
|
||||
{
|
||||
id: 'add_a_correction',
|
||||
value: 'add_a_correction',
|
||||
label: __(
|
||||
'Add a correction',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
description: __(
|
||||
'Adds an additional line item with the missing amount.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'do_not_send_line_items',
|
||||
value: 'do_not_send_line_items',
|
||||
label: __(
|
||||
'Do not send line items',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
description: __(
|
||||
'Resubmit the transaction without line item details.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
] }
|
||||
actionProps={ {
|
||||
name: 'paypal_settings_mismatch',
|
||||
key: 'subtotalMismatchFallback',
|
||||
currentValue: settings.subtotalMismatchFallback,
|
||||
callback: updateFormValue,
|
||||
} }
|
||||
/>
|
||||
>
|
||||
<ControlRadioGroup
|
||||
options={ subtotalAdjustmentChoices }
|
||||
value={ subtotalAdjustment }
|
||||
onChange={ setSubtotalAdjustment }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
|
||||
<ToggleSettingsBlock
|
||||
title={ __(
|
||||
'Instant payments only',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'If enabled, PayPal will not allow buyers to use funding sources that take additional time to complete, such as eChecks.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
value: settings.savePaypalAndVenmo,
|
||||
callback: updateFormValue,
|
||||
key: 'savePaypalAndVenmo',
|
||||
} }
|
||||
/>
|
||||
<SettingsBlock>
|
||||
<ControlToggleButton
|
||||
label={ __(
|
||||
'Instant payments only',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'If enabled, PayPal will not allow buyers to use funding sources that take additional time to complete, such as eChecks.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
value={ savePaypalAndVenmo }
|
||||
onChange={ setSavePaypalAndVenmo }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
|
||||
<InputSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __( 'Brand name', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'What business name to show to your buyers during checkout and on receipts.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
value: settings.brandName,
|
||||
callback: updateFormValue,
|
||||
key: 'brandName',
|
||||
placeholder: __(
|
||||
>
|
||||
<ControlTextInput
|
||||
value={ brandName }
|
||||
onChange={ setBrandName }
|
||||
placeholder={ __(
|
||||
'Brand name',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
} }
|
||||
order={ [ 'title', 'description', 'action' ] }
|
||||
/>
|
||||
) }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
|
||||
<InputSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __( 'Soft Descriptor', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
"The dynamic text used to construct the statement descriptor that appears on a payer's card statement. Applies to PayPal and Credit Card transactions. Max value of 22 characters.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
value: settings.softDescriptor,
|
||||
callback: updateFormValue,
|
||||
key: 'softDescriptor',
|
||||
placeholder: __(
|
||||
>
|
||||
<ControlTextInput
|
||||
value={ softDescriptor }
|
||||
onChange={ setSoftDescriptor }
|
||||
placeholder={ __(
|
||||
'Soft Descriptor',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
} }
|
||||
order={ [ 'title', 'description', 'action' ] }
|
||||
/>
|
||||
) }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
|
||||
<RadioSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __(
|
||||
'PayPal landing page',
|
||||
'woocommerce-paypal-payments'
|
||||
|
@ -126,71 +110,32 @@ const PaypalSettings = ( { updateFormValue, settings } ) => {
|
|||
'Determine which experience a buyer sees when they click the PayPal button.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
options={ [
|
||||
{
|
||||
id: 'no_preference',
|
||||
value: 'no_reference',
|
||||
label: __(
|
||||
'No preference',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
description: __(
|
||||
'Shows the buyer the PayPal login for a recognized PayPal buyer.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'login_page',
|
||||
value: 'login_page',
|
||||
label: __(
|
||||
'Login page',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
description: __(
|
||||
'Always show the buyer the PayPal login screen.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'guest_checkout_page',
|
||||
value: 'guest_checkout_page',
|
||||
label: __(
|
||||
'Guest checkout page',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
description: __(
|
||||
'Always show the buyer the guest checkout fields first.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
] }
|
||||
actionProps={ {
|
||||
name: 'paypal_settings_landing',
|
||||
key: 'paypalLandingPage',
|
||||
currentValue: settings.paypalLandingPage,
|
||||
callback: updateFormValue,
|
||||
} }
|
||||
/>
|
||||
>
|
||||
<ControlRadioGroup
|
||||
options={ landingPageChoices }
|
||||
value={ landingPage }
|
||||
onChange={ setLandingPage }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
|
||||
<SelectSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __( 'Button Language', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
"If left blank, PayPal and other buttons will present in the user's detected language. Enter a language here to force all buttons to display in that language.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
value: settings.buttonLanguage,
|
||||
callback: updateFormValue,
|
||||
options: languagesExample,
|
||||
key: 'buttonLanguage',
|
||||
placeholder: __(
|
||||
>
|
||||
<ControlSelect
|
||||
options={ languagesExample }
|
||||
value={ buttonLanguage }
|
||||
onChange={ setButtonLanguage }
|
||||
placeholder={ __(
|
||||
'Browser language',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
} }
|
||||
order={ [ 'title', 'description', 'action' ] }
|
||||
/>
|
||||
</AccordionSettingsBlock>
|
||||
) }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -201,4 +146,50 @@ const languagesExample = [
|
|||
{ value: 'it', label: 'Italian' },
|
||||
];
|
||||
|
||||
const subtotalAdjustmentChoices = [
|
||||
{
|
||||
value: 'correction',
|
||||
label: __( 'Add a correction', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Adds an additional line item with the missing amount.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'no_details',
|
||||
label: __( 'Do not send line items', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Resubmit the transaction without line item details.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const landingPageChoices = [
|
||||
{
|
||||
value: 'any',
|
||||
label: __( 'No preference', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Shows the buyer the PayPal login for a recognized PayPal buyer.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'login',
|
||||
label: __( 'Login page', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Always show the buyer the PayPal login screen.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'guest_checkout',
|
||||
label: __( 'Guest checkout page', 'woocommerce-paypal-payments' ),
|
||||
description: __(
|
||||
'Always show the buyer the guest checkout fields first.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export default PaypalSettings;
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import {
|
||||
Header,
|
||||
SettingsBlock,
|
||||
ToggleSettingsBlock,
|
||||
Title,
|
||||
Description,
|
||||
} from '../../../../ReusableComponents/SettingsBlocks';
|
||||
|
||||
const SavePaymentMethods = ( { updateFormValue, settings } ) => {
|
||||
return (
|
||||
<SettingsBlock className="ppcp-r-settings-block--save-payment-methods">
|
||||
<Header>
|
||||
<Title>
|
||||
{ __(
|
||||
'Save payment methods',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Title>
|
||||
<Description>
|
||||
{ __(
|
||||
"Securely store customers' payment methods for future payments and subscriptions, simplifying checkout and enabling recurring transactions.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</Description>
|
||||
</Header>
|
||||
|
||||
<ToggleSettingsBlock
|
||||
title={ __(
|
||||
'Save PayPal and Venmo',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={
|
||||
<div
|
||||
dangerouslySetInnerHTML={ {
|
||||
__html: sprintf(
|
||||
/* translators: 1: URL to Pay Later documentation, 2: URL to Alternative Payment Methods documentation */
|
||||
__(
|
||||
'Securely store your customers\' PayPal accounts for a seamless checkout experience. <br />This will disable all <a target="_blank" rel="noreferrer" href="%1$s">Pay Later</a> features and <a target="_blank" rel="noreferrer" href="%2$s">Alternative Payment Methods</a> on your site.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later',
|
||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods'
|
||||
),
|
||||
} }
|
||||
/>
|
||||
}
|
||||
actionProps={ {
|
||||
value: settings.savePaypalAndVenmo,
|
||||
callback: updateFormValue,
|
||||
key: 'savePaypalAndVenmo',
|
||||
} }
|
||||
/>
|
||||
|
||||
<ToggleSettingsBlock
|
||||
title={ __(
|
||||
'Save Credit and Debit Cards',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
"Securely store your customer's credit card.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'saveCreditCardAndDebitCard',
|
||||
value: settings.saveCreditCardAndDebitCard,
|
||||
} }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default SavePaymentMethods;
|
|
@ -1,9 +1,8 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import { CommonHooks } from '../../../../../../data';
|
||||
import {
|
||||
SettingsBlock,
|
||||
Title,
|
||||
} from '../../../../../ReusableComponents/SettingsBlocks';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import { Title } from '../../../../../ReusableComponents/Elements';
|
||||
|
||||
const HooksTableBlock = () => {
|
||||
const { webhooks } = CommonHooks.useWebhooks();
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { useState } from '@wordpress/element';
|
||||
import { STORE_NAME } from '../../../../../../data/common';
|
||||
import { ButtonSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { useState } from '@wordpress/element';
|
||||
import { store as noticesStore } from '@wordpress/notices';
|
||||
|
||||
import { STORE_NAME } from '../../../../../../data/common';
|
||||
import { ControlButton } from '../../../../../ReusableComponents/Controls';
|
||||
import {
|
||||
NOTIFICATION_ERROR,
|
||||
NOTIFICATION_SUCCESS,
|
||||
} from '../../../../../ReusableComponents/Icons';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
|
||||
const ResubscribeBlock = () => {
|
||||
const { createSuccessNotice, createErrorNotice } =
|
||||
|
@ -47,7 +49,7 @@ const ResubscribeBlock = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<ButtonSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __(
|
||||
'Resubscribe webhooks',
|
||||
'woocommerce-paypal-payments'
|
||||
|
@ -56,17 +58,18 @@ const ResubscribeBlock = () => {
|
|||
'Click to remove the current webhook subscription and subscribe again, for example, if the website domain or URL structure changed.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
separatorAndGap={ false }
|
||||
actionProps={ {
|
||||
buttonType: 'secondary',
|
||||
isBusy: resubscribing,
|
||||
callback: () => startResubscribingWebhooks(),
|
||||
value: __(
|
||||
horizontalLayout={ true }
|
||||
>
|
||||
<ControlButton
|
||||
type={ 'secondary' }
|
||||
isBusy={ resubscribing }
|
||||
onClick={ () => startResubscribingWebhooks() }
|
||||
buttonLabel={ __(
|
||||
'Resubscribe webhooks',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
} }
|
||||
/>
|
||||
) }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { useState } from '@wordpress/element';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { ButtonSettingsBlock } from '../../../../../ReusableComponents/SettingsBlocks';
|
||||
import { useDispatch } from '@wordpress/data';
|
||||
import { store as noticesStore } from '@wordpress/notices';
|
||||
|
||||
import { ControlButton } from '../../../../../ReusableComponents/Controls';
|
||||
import { CommonHooks } from '../../../../../../data';
|
||||
import {
|
||||
NOTIFICATION_ERROR,
|
||||
NOTIFICATION_SUCCESS,
|
||||
} from '../../../../../ReusableComponents/Icons';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
|
||||
const SimulationBlock = () => {
|
||||
const {
|
||||
|
@ -107,20 +109,24 @@ const SimulationBlock = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<ButtonSettingsBlock
|
||||
<SettingsBlock
|
||||
title={ __( 'Test webhooks', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Send a test-webhook from PayPal to confirm that webhooks are being received and processed correctly.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
separatorAndGap={ false }
|
||||
actionProps={ {
|
||||
buttonType: 'secondary',
|
||||
isBusy: simulating,
|
||||
callback: () => startSimulation( 30 ),
|
||||
value: __( 'Simulate webhooks', 'woocommerce-paypal-payments' ),
|
||||
} }
|
||||
/>
|
||||
horizontalLayout={ true }
|
||||
>
|
||||
<ControlButton
|
||||
type={ 'secondary' }
|
||||
isBusy={ simulating }
|
||||
onClick={ () => startSimulation( 30 ) }
|
||||
buttonLabel={ __(
|
||||
'Simulate webhooks',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
export default SimulationBlock;
|
||||
|
|
|
@ -1,68 +1,53 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
AccordionSettingsBlock,
|
||||
Description,
|
||||
Header,
|
||||
Title,
|
||||
ToggleSettingsBlock,
|
||||
} from '../../../../../ReusableComponents/SettingsBlocks';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlocks/SettingsBlock';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
import { ControlToggleButton } from '../../../../../ReusableComponents/Controls';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import Accordion from '../../../../../ReusableComponents/AccordionSection';
|
||||
|
||||
import SimulationBlock from './SimulationBlock';
|
||||
import ResubscribeBlock from './ResubscribeBlock';
|
||||
import HooksTableBlock from './HooksTableBlock';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
|
||||
const Troubleshooting = () => {
|
||||
const { logging, setLogging } = SettingsHooks.useSettings();
|
||||
|
||||
const Troubleshooting = ( { updateFormValue, settings } ) => {
|
||||
return (
|
||||
<AccordionSettingsBlock
|
||||
className="ppcp-r-settings-block--troubleshooting"
|
||||
<Accordion
|
||||
className="ppcp--troubleshooting"
|
||||
title={ __( 'Troubleshooting', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Access tools to help debug and resolve issues.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'payNowExperience',
|
||||
value: settings.payNowExperience,
|
||||
} }
|
||||
>
|
||||
<ToggleSettingsBlock
|
||||
title={ __( 'Logging', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Log additional debugging information in the WooCommerce logs that can assist technical staff to determine issues.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'logging',
|
||||
value: settings.logging,
|
||||
} }
|
||||
/>
|
||||
<SettingsBlock>
|
||||
<Header>
|
||||
<Title>
|
||||
{ __( 'Webhooks', 'woocommerce-paypal-payments' ) }
|
||||
</Title>
|
||||
<Description>
|
||||
{ __(
|
||||
'The following PayPal webhooks are subscribed. More information about the webhooks is available in the',
|
||||
'woocommerce-paypal-payments'
|
||||
) }{ ' ' }
|
||||
<a href="https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status">
|
||||
{ __(
|
||||
'Webhook Status documentation',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
</a>
|
||||
.
|
||||
</Description>
|
||||
</Header>
|
||||
<ControlToggleButton
|
||||
label={ __( 'Logging', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Log additional debugging information in the WooCommerce logs that can assist technical staff to determine issues.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
value={ logging }
|
||||
onChange={ setLogging }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
|
||||
<SettingsBlock
|
||||
title={ __( 'Webhooks', 'woocommerce-paypal-payments' ) }
|
||||
description={ sprintf(
|
||||
__(
|
||||
'The following PayPal webhooks are subscribed. More information about the webhooks is available in the <a href="%s">Webhook Status documentation</a>.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#webhook-status'
|
||||
) }
|
||||
>
|
||||
<HooksTableBlock />
|
||||
<ResubscribeBlock />
|
||||
<SimulationBlock />
|
||||
</SettingsBlock>
|
||||
</AccordionSettingsBlock>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
InputSettingsBlock,
|
||||
ToggleSettingsBlock,
|
||||
} from '../../../ReusableComponents/SettingsBlocks';
|
||||
import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
||||
import OrderIntent from './Blocks/OrderIntent';
|
||||
import SavePaymentMethods from './Blocks/SavePaymentMethods';
|
||||
|
||||
const CommonSettings = ( { updateFormValue, settings } ) => {
|
||||
return (
|
||||
<SettingsCard
|
||||
icon="icon-settings-common.svg"
|
||||
title={ __( 'Common settings', 'woocommerce-paypal-payments' ) }
|
||||
className="ppcp-r-settings-card ppcp-r-settings-card--common-settings"
|
||||
description={ __(
|
||||
'Customize key features to tailor your PayPal experience.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<InputSettingsBlock
|
||||
title="Invoice Prefix"
|
||||
supplementaryLabel={ __(
|
||||
'(Recommended)',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description="Add a unique prefix to invoice numbers for site-specific tracking (recommended)."
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'invoicePrefix',
|
||||
value: settings.invoicePrefix,
|
||||
placeholder: __(
|
||||
'Input prefix',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
} }
|
||||
/>
|
||||
|
||||
<OrderIntent
|
||||
settings={ settings }
|
||||
updateFormValue={ updateFormValue }
|
||||
/>
|
||||
|
||||
<SavePaymentMethods
|
||||
updateFormValue={ updateFormValue }
|
||||
settings={ settings }
|
||||
/>
|
||||
|
||||
<ToggleSettingsBlock
|
||||
title={ __(
|
||||
'Pay Now Experience',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Let PayPal customers skip the Order Review page by selecting shipping options directly within PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
actionProps={ {
|
||||
callback: updateFormValue,
|
||||
key: 'payNowExperience',
|
||||
value: settings.payNowExperience,
|
||||
} }
|
||||
/>
|
||||
</SettingsCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommonSettings;
|
|
@ -1,49 +0,0 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
||||
import { CommonHooks } from '../../../../data';
|
||||
import TitleBadge, {
|
||||
TITLE_BADGE_NEGATIVE,
|
||||
TITLE_BADGE_POSITIVE,
|
||||
} from '../../../ReusableComponents/TitleBadge';
|
||||
import ConnectionInfo from '../../../ReusableComponents/ConnectionInfo';
|
||||
const ConnectionStatus = () => {
|
||||
const { merchant } = CommonHooks.useMerchantInfo();
|
||||
return (
|
||||
<SettingsCard
|
||||
className="ppcp-r-tab-overview-support"
|
||||
title={ __( 'Connection status', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Your PayPal account connection details',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<div className="ppcp-r-connection-status">
|
||||
<div className="ppcp-r-connection-status__status">
|
||||
<div className="ppcp-r-connection-status__status-status">
|
||||
{ merchant.isConnected ? (
|
||||
<TitleBadge
|
||||
type={ TITLE_BADGE_POSITIVE }
|
||||
text={ __(
|
||||
'Active',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
/>
|
||||
) : (
|
||||
<TitleBadge
|
||||
type={ TITLE_BADGE_NEGATIVE }
|
||||
text={ __(
|
||||
'Not Activated',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
/>
|
||||
) }
|
||||
</div>
|
||||
</div>
|
||||
{ merchant.isConnected && (
|
||||
<ConnectionInfo connectionStatusDataDefault={ merchant } />
|
||||
) }
|
||||
</div>
|
||||
</SettingsCard>
|
||||
);
|
||||
};
|
||||
export default ConnectionStatus;
|
|
@ -1,31 +1,19 @@
|
|||
import { Button } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import { CommonHooks, StylingHooks } from '../../../../data';
|
||||
import TopNavigation from '../../../ReusableComponents/TopNavigation';
|
||||
import BusyStateWrapper from '../../../ReusableComponents/BusyStateWrapper';
|
||||
import { useSaveSettings } from '../../../../hooks/useSaveSettings';
|
||||
|
||||
const SettingsNavigation = () => {
|
||||
const { withActivity } = CommonHooks.useBusyState();
|
||||
|
||||
// Todo: Implement other stores here.
|
||||
const { persist: persistStyling } = StylingHooks.useStore();
|
||||
|
||||
const handleSaveClick = () => {
|
||||
// Todo: Add other stores here.
|
||||
withActivity(
|
||||
'persist-styling',
|
||||
'Save styling details',
|
||||
persistStyling
|
||||
);
|
||||
};
|
||||
const { persistAll } = useSaveSettings();
|
||||
|
||||
const title = __( 'PayPal Payments', 'woocommerce-paypal-payments' );
|
||||
|
||||
return (
|
||||
<TopNavigation title={ title } exitOnTitleClick={ true }>
|
||||
<BusyStateWrapper>
|
||||
<Button variant="primary" onClick={ handleSaveClick }>
|
||||
<Button variant="primary" onClick={ persistAll }>
|
||||
{ __( 'Save', 'woocommerce-paypal-payments' ) }
|
||||
</Button>
|
||||
</BusyStateWrapper>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import { ControlTextInput } from '../../../../../ReusableComponents/Controls';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
|
||||
const InvoicePrefix = () => {
|
||||
const { invoicePrefix, setInvoicePrefix } = SettingsHooks.useSettings();
|
||||
|
||||
return (
|
||||
<SettingsBlock
|
||||
title="Invoice Prefix"
|
||||
titleSuffix={ __( '(Recommended)', 'woocommerce-paypal-payments' ) }
|
||||
className="ppcp--invoice-prefix"
|
||||
>
|
||||
<ControlTextInput
|
||||
placeholder={ __(
|
||||
'Input prefix',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
onChange={ setInvoicePrefix }
|
||||
value={ invoicePrefix }
|
||||
description="Add a unique prefix to invoice numbers for site-specific tracking (recommended)."
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default InvoicePrefix;
|
|
@ -0,0 +1,42 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import { ControlToggleButton } from '../../../../../ReusableComponents/Controls';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
|
||||
const OrderIntent = () => {
|
||||
const {
|
||||
authorizeOnly,
|
||||
setAuthorizeOnly,
|
||||
captureVirtualOnlyOrders,
|
||||
setCaptureVirtualOnlyOrders,
|
||||
} = SettingsHooks.useSettings();
|
||||
|
||||
return (
|
||||
<SettingsBlock
|
||||
title={ __( 'Order Intent', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Choose between immediate capture or authorization-only, with manual capture in the Order section.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
className="ppcp--order-intent"
|
||||
>
|
||||
<ControlToggleButton
|
||||
label={ __( 'Authorize Only', 'woocommerce-paypal-payments' ) }
|
||||
onChange={ setAuthorizeOnly }
|
||||
value={ authorizeOnly }
|
||||
/>
|
||||
|
||||
<ControlToggleButton
|
||||
label={ __(
|
||||
'Capture Virtual-Only Orders',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
onChange={ setCaptureVirtualOnlyOrders }
|
||||
value={ captureVirtualOnlyOrders }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderIntent;
|
|
@ -0,0 +1,29 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import { ControlToggleButton } from '../../../../../ReusableComponents/Controls';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
|
||||
const PayNowExperience = () => {
|
||||
const { payNowExperience, setPayNowExperience } =
|
||||
SettingsHooks.useSettings();
|
||||
|
||||
return (
|
||||
<SettingsBlock className="ppcp--pay-now-experience">
|
||||
<ControlToggleButton
|
||||
label={ __(
|
||||
'Pay Now Experience',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
'Let PayPal customers skip the Order Review page by selecting shipping options directly within PayPal.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
onChange={ setPayNowExperience }
|
||||
value={ payNowExperience }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default PayNowExperience;
|
|
@ -0,0 +1,61 @@
|
|||
import { __, sprintf } from '@wordpress/i18n';
|
||||
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import { ControlToggleButton } from '../../../../../ReusableComponents/Controls';
|
||||
import { SettingsHooks } from '../../../../../../data';
|
||||
|
||||
const SavePaymentMethods = () => {
|
||||
const {
|
||||
savePaypalAndVenmo,
|
||||
setSavePaypalAndVenmo,
|
||||
saveCardDetails,
|
||||
setSaveCardDetails,
|
||||
} = SettingsHooks.useSettings();
|
||||
|
||||
return (
|
||||
<SettingsBlock
|
||||
title={ __(
|
||||
'Save payment methods',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
"Securely store customers' payment methods for future payments and subscriptions, simplifying checkout and enabling recurring transactions.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
className="ppcp--save-payment-methods"
|
||||
>
|
||||
<ControlToggleButton
|
||||
label={ __(
|
||||
'Save PayPal and Venmo',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ sprintf(
|
||||
/* translators: 1: URL to Pay Later documentation, 2: URL to Alternative Payment Methods documentation */
|
||||
__(
|
||||
'Securely store your customers\' PayPal accounts for a seamless checkout experience. <br />This will disable all <a target="_blank" rel="noreferrer" href="%1$s">Pay Later</a> features and <a target="_blank" rel="noreferrer" href="%2$s">Alternative Payment Methods</a> on your site.',
|
||||
'woocommerce-paypal-payments'
|
||||
),
|
||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#pay-later',
|
||||
'https://woocommerce.com/document/woocommerce-paypal-payments/#alternative-payment-methods'
|
||||
) }
|
||||
value={ savePaypalAndVenmo }
|
||||
onChange={ setSavePaypalAndVenmo }
|
||||
/>
|
||||
|
||||
<ControlToggleButton
|
||||
label={ __(
|
||||
'Save Credit and Debit Cards',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
description={ __(
|
||||
"Securely store your customer's credit card.",
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
onChange={ setSaveCardDetails }
|
||||
value={ saveCardDetails }
|
||||
/>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default SavePaymentMethods;
|
|
@ -0,0 +1,26 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import SettingsCard from '../../../../ReusableComponents/SettingsCard';
|
||||
import OrderIntent from './Blocks/OrderIntent';
|
||||
import SavePaymentMethods from './Blocks/SavePaymentMethods';
|
||||
import InvoicePrefix from './Blocks/InvoicePrefix';
|
||||
import PayNowExperience from './Blocks/PayNowExperience';
|
||||
|
||||
const CommonSettings = () => (
|
||||
<SettingsCard
|
||||
icon="icon-settings-common.svg"
|
||||
title={ __( 'Common settings', 'woocommerce-paypal-payments' ) }
|
||||
className="ppcp-r-settings-card ppcp-r-settings-card--common-settings"
|
||||
description={ __(
|
||||
'Customize key features to tailor your PayPal experience.',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<InvoicePrefix />
|
||||
<OrderIntent />
|
||||
<SavePaymentMethods />
|
||||
<PayNowExperience />
|
||||
</SettingsCard>
|
||||
);
|
||||
|
||||
export default CommonSettings;
|
|
@ -0,0 +1,50 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import SettingsCard from '../../../../ReusableComponents/SettingsCard';
|
||||
import { CommonHooks } from '../../../../../data';
|
||||
import ConnectionStatusBadge from './Parts/ConnectionStatusBadge';
|
||||
import SettingsBlock from '../../../../ReusableComponents/SettingsBlock';
|
||||
import { ControlStaticValue } from '../../../../ReusableComponents/Controls';
|
||||
|
||||
const ConnectionStatus = () => {
|
||||
const { merchant } = CommonHooks.useMerchantInfo();
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
className="ppcp-connection-details ppcp--value-list"
|
||||
title={ __( 'Connection status', 'woocommerce-paypal-payments' ) }
|
||||
description={ __(
|
||||
'Your PayPal account connection details',
|
||||
'woocommerce-paypal-payments'
|
||||
) }
|
||||
>
|
||||
<SettingsBlock>
|
||||
<ControlStaticValue
|
||||
value={
|
||||
<ConnectionStatusBadge
|
||||
isActive={ merchant.isConnected }
|
||||
isSandbox={ merchant.isSandbox }
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SettingsBlock>
|
||||
<SettingsBlock
|
||||
title={ __( 'Merchant ID', 'woocommerce-paypal-payments' ) }
|
||||
>
|
||||
<ControlStaticValue value={ merchant.id } />
|
||||
</SettingsBlock>
|
||||
<SettingsBlock
|
||||
title={ __( 'Email address', 'woocommerce-paypal-payments' ) }
|
||||
>
|
||||
<ControlStaticValue value={ merchant.email } />
|
||||
</SettingsBlock>
|
||||
<SettingsBlock
|
||||
title={ __( 'Client ID', 'woocommerce-paypal-payments' ) }
|
||||
>
|
||||
<ControlStaticValue value={ merchant.clientId } />
|
||||
</SettingsBlock>
|
||||
</SettingsCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectionStatus;
|
|
@ -1,15 +1,18 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
import SettingsCard from '../../../ReusableComponents/SettingsCard';
|
||||
import SettingsCard from '../../../../ReusableComponents/SettingsCard';
|
||||
import {
|
||||
Content,
|
||||
ContentWrapper,
|
||||
} from '../../../ReusableComponents/SettingsBlocks';
|
||||
import ConnectionDetails from './Blocks/ConnectionDetails';
|
||||
import Troubleshooting from './Blocks/Troubleshooting/Troubleshooting';
|
||||
import PaypalSettings from './Blocks/PaypalSettings';
|
||||
import OtherSettings from './Blocks/OtherSettings';
|
||||
} from '../../../../ReusableComponents/Elements';
|
||||
import ConnectionDetails from '../../../Overview/TabSettingsElements/Blocks/ConnectionDetails';
|
||||
import Troubleshooting from '../../../Overview/TabSettingsElements/Blocks/Troubleshooting/Troubleshooting';
|
||||
import PaypalSettings from '../../../Overview/TabSettingsElements/Blocks/PaypalSettings';
|
||||
import OtherSettings from '../../../Overview/TabSettingsElements/Blocks/OtherSettings';
|
||||
|
||||
const ExpertSettings = () => {
|
||||
const settings = {}; // dummy object
|
||||
const updateFormValue = () => {}; // dummy function
|
||||
|
||||
const ExpertSettings = ( { updateFormValue, settings } ) => {
|
||||
return (
|
||||
<SettingsCard
|
||||
icon="icon-settings-expert.svg"
|
|
@ -0,0 +1,25 @@
|
|||
import { __ } from '@wordpress/i18n';
|
||||
|
||||
import TitleBadge, {
|
||||
TITLE_BADGE_NEGATIVE,
|
||||
TITLE_BADGE_POSITIVE,
|
||||
} from '../../../../../ReusableComponents/TitleBadge';
|
||||
|
||||
const ConnectionStatusBadge = ( { isActive, isSandbox } ) => {
|
||||
if ( isActive ) {
|
||||
const label = isSandbox
|
||||
? __( 'Sandbox Mode', 'woocommerce-paypal-payments' )
|
||||
: __( 'Active', 'woocommerce-paypal-payments' );
|
||||
|
||||
return <TitleBadge type={ TITLE_BADGE_POSITIVE } text={ label } />;
|
||||
}
|
||||
|
||||
return (
|
||||
<TitleBadge
|
||||
type={ TITLE_BADGE_NEGATIVE }
|
||||
text={ __( 'Not Connected', 'woocommerce-paypal-payments' ) }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConnectionStatusBadge;
|
|
@ -1,10 +1,10 @@
|
|||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlocks/SettingsBlock';
|
||||
import SettingsBlock from '../../../../../ReusableComponents/SettingsBlock';
|
||||
import {
|
||||
Description,
|
||||
Header,
|
||||
Title,
|
||||
Content,
|
||||
} from '../../../../../ReusableComponents/SettingsBlocks';
|
||||
} from '../../../../../ReusableComponents/Elements';
|
||||
|
||||
const StylingSection = ( {
|
||||
title,
|
||||
|
@ -26,7 +26,9 @@ const StylingSection = ( {
|
|||
<Description>{ description }</Description>
|
||||
</Header>
|
||||
|
||||
<Content className="section-content">{ children }</Content>
|
||||
<Content asCard={ false } className="section-content">
|
||||
{ children }
|
||||
</Content>
|
||||
</SettingsBlock>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ const StylingSectionWithCheckboxes = ( {
|
|||
onChange,
|
||||
children,
|
||||
} ) => {
|
||||
className = classNames( 'has-checkboxes', className );
|
||||
className = classNames( 'ppcp--has-checkboxes', className );
|
||||
|
||||
return (
|
||||
<StylingSection
|
||||
|
|
|
@ -14,7 +14,7 @@ const StylingSectionWithRadiobuttons = ( {
|
|||
onChange,
|
||||
children,
|
||||
} ) => {
|
||||
className = classNames( 'has-radio-buttons', className );
|
||||
className = classNames( 'ppcp--has-radio-buttons', className );
|
||||
|
||||
return (
|
||||
<StylingSection
|
||||
|
|
|
@ -13,7 +13,7 @@ const StylingSectionWithSelect = ( {
|
|||
onChange,
|
||||
children,
|
||||
} ) => {
|
||||
className = classNames( 'has-select', className );
|
||||
className = classNames( 'ppcp--has-select', className );
|
||||
|
||||
return (
|
||||
<StylingSection
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue