How to build your plugin



Settings
View

APP_MODEL
powrs' users (aka wobblies)
powrs' users' users (aka wobblies' wobblies)
APP_MODEL.attributes


APP_MODEL.attributes.data



Settings consist of PowrSection-s
Templates and Import Section

Content Section


Settings Section

Payment Section

Design Section

PowrSection-s in code



Templates and Import Section
Each config file exports an array of JavaScript objects
const sc = POWr.simpleCopies;
import currencies from '@./../constants/currencies.js';
import {PAYPAL_URI} from '@/../helpers/constants';
// const prePath = './../../../modules/react_components';
//containers
import PowrDrilldown from '@./../modules/react_components/powr_drilldown';
import PowrPrototypeSection from '@./../modules/react_components/powr_prototype_section';
// form elements
import PowrButton from '@./../modules/react_components/powr_button';
import PowrToggle from '@./../modules/react_components/powr_toggle';
import PowrSelect from '@./../modules/react_components/powr_select';
import PowrMultitoggle from '@./../modules/react_components/powr_multitoggle';
import PowrTextInput from '@./../modules/react_components/powr_text_input';
import PowrSettingsLabel from '@./../modules/react_components/powr_settings_label';
// utils
import PowrBannerNotification from '@./../modules/react_components/utils/powr_banner_notification';
import PowrMultiInputs from '@./../modules/react_components/containers/powr_multi_inputs';
import {removeDiscountCodesFeature, showPurpleDemoStar} from '@/../config/apps/form_builder/unblockedFeatures';
import { isInABTest } from '../../../modules/powr_helpers';
// import PaymentProductInfo from '@./../modules/react_components/utils/payment_product_info';
const namespace = 'paypal';
const paypal_url = PAYPAL_URI[window.ENVIRONMENT];
const timeFrames = [
{
label: 'Days',
value: 'D',
},
{
label: 'Weeks',
value: 'W',
},
{
label: 'Months',
value: 'M',
},
{
label: 'Years',
value: 'Y',
},
];
const subscriptionDetails = [
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.product_cost'),
namespace: `${namespace}ItemCost`,
placeholder: sc('app_settings.payment.enter_a_price'),
validatePrice: true,
validateMax: 10000000,
addFormElementPadding: true,
},
},
{
powrComponent: PowrMultiInputs,
passedProps: {
label: sc('app_settings.payment.repeat_payment_every'),
addFormElementPadding: true,
innerComponents: [
{
powrComponent: PowrTextInput,
passedProps: {
namespace: `${namespace}SubscriptionDuration`,
placeholder: '30',
validateInteger: true,
validateLengthMax: 100,
},
},
{
powrComponent: PowrSelect,
passedProps: {
namespace: `${namespace}SubscriptionDurationUnits`,
options: timeFrames,
},
},
],
},
},
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.trial_on'),
namespace: `${namespace}TrialOn`,
addFormElementPadding: true,
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: `model.${namespace}TrialOn`,
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: 'div',
passedProps: {},
conditionals: [
{
key: `observableStore.model.attributes.${namespace}TrialOn`,
value: true,
},
],
innerComponents: [
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.trial_price'),
namespace: `${namespace}TrialPrice`,
addFormElementPadding: true,
placeholder: '0.00',
validatePrice: true,
validateMax: 10000000,
},
},
{
powrComponent: PowrMultiInputs,
passedProps: {
label: sc('app_settings.payment.trial_length'),
addFormElementPadding: true,
innerComponents: [
{
powrComponent: PowrTextInput,
passedProps: {
namespace: `${namespace}TrialDuration`,
addFormElementPadding: true,
placeholder: '30',
validateInteger: true,
validateLengthMax: 100,
},
},
{
powrComponent: PowrSelect,
passedProps: {
namespace: `${namespace}TrialDurationUnits`,
addFormElementPadding: true,
options: timeFrames,
},
},
],
},
},
],
},
];
const nonSubscriptionDetails = [
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.product_cost'),
namespace: `${namespace}ItemCost`,
placeholder: sc('app_settings.payment.enter_a_price'),
validatePrice: true,
validateMax: 1000000,
addFormElementPadding: true,
},
},
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.allow_buyer_choose_price'),
description: sc('components.multiple_apps_descriptions.allow_buyer_choose_price_description'),
namespace: `${namespace}AllowUserToSpecifyPrice`,
addFormElementPadding: true,
},
},
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.show_quantity_input'),
namespace: 'showQuantityInput',
addFormElementPadding: true,
},
},
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.default_quantity'),
namespace: 'paypalQuantity',
placeholder: '1',
validateInteger: true,
validateMax: 1000000,
addFormElementPadding: true,
},
},
];
const discountPrototype = position => {
return [
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.discount_code_prototype'),
namespace: 'discountCode',
placeholder: sc('app_settings.payment.enter_discount_code'),
validateLengthMax: 1000,
addFormElementPadding: true,
handleChangeComplete: (value, observableStore) => {
observableStore.model.attributes.discounts[position].discountCode = value;
observableStore.forceUpdate();
},
trimSpaces: true,
},
dynamicProps: [
{
propName: 'value',
key: `observableStore.model.attributes.discounts.${position}.discountCode`,
},
{
propName: 'featurePremiumStatus',
evaluate: () => {
if (isInABTest('ab_starter_removes_watermark')) {
return 'pro';
}
return 'premium';
}
}
],
},
{
powrComponent: PowrMultiInputs,
passedProps: {
label: sc('app_settings.payment.discount_amount'),
addFormElementPadding: true,
innerComponents: [
{
powrComponent: PowrMultitoggle,
passedProps: {
namespace: 'discountType',
options: [['$', 'flat'], ['%', 'rate']],
size: 3,
handleChangeComplete: (value, observableStore) => {
observableStore.model.attributes.discounts[position].discountType = value;
observableStore.forceUpdate();
},
},
dynamicProps: [
{
propName: 'value',
key: `observableStore.model.attributes.discounts.${position}.discountType`,
},
],
},
{
powrComponent: PowrTextInput,
passedProps: {
namespace: 'discountAmount',
placeholder: sc('app_settings.payment.enter_value'),
validatePrice: true,
size: 9,
handleChangeComplete: (value, observableStore) => {
observableStore.model.attributes.discounts[position].discountAmount = value;
observableStore.forceUpdate();
},
},
dynamicProps: [
{
propName: 'value',
key: `observableStore.model.attributes.discounts.${position}.discountAmount`,
},
],
},
],
},
},
];
};
const checkPaymentTypes = (paymentType1, paymentType2) => {
return observableStore => {
const allDisabled = !observableStore.model.attributes[paymentType1] && !observableStore.model.attributes[paymentType2];
if (allDisabled) {
return 'free';
}
if (isInABTest('ab_starter_removes_watermark')) {
return 'pro';
}
return 'premium';
};
};
const connectPaymentAccounts = [
{
powrComponent: 'div',
passedProps: {
className: 'form-element small-warning',
dangerouslySetInnerHTML: {__html: `<i class='fa fa-warning failure'></i> ${sc('formbuilder.payment.connect_warning')}`},
},
conditionals: [
{
key: 'observableStore.model.attributes',
check: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
{
powrComponent: PowrToggle,
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
passedProps: {
label: sc('app_settings.payment.allow_paypal_payments'),
namespace: 'paypalEnabled',
addFormElementPadding: true,
helpText: sc('app_settings.payment.paypal_connect_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.paypalEnabled',
value,
refreshSettings: true,
});
},
},
dynamicProps:[
{
propName: 'featurePremiumStatus',
evaluateWithStore: checkPaymentTypes('stripeEnabled', 'offlineEnabled')
},
]
},
{
powrComponent: PowrToggle,
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
{
key: 'observableStore.model.attributes.paypalEnabled',
value: true,
},
],
passedProps: {
label: sc('app_settings.payment.enable_smart_buttons'),
namespace: 'smartButtonsEnabled',
addFormElementPadding: true,
helpText: sc('formbuilder.payment.smart_button_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.smartButtonsEnabled',
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.paypal_account'),
namespace: `${namespace}PaypalAccount`,
addFormElementPadding: true,
placeholder: sc('app_settings.payment.enter_paypal_email'),
type: 'email',
},
conditionals: [
{
key: 'observableStore.model.attributes.paypalEnabled',
value: true,
},
],
},
{
powrComponent: 'div',
conditionals: [
{
key: 'observableStore.model.attributes.paypalEnabled',
value: true,
},
{
key: 'observableStore.model.attributes.paypalPaypalAccount',
check: paypalPaypalAccount => {
return !paypalPaypalAccount;
},
},
],
passedProps: {
className: 'small-help text-center',
},
innerComponents: [
{
powrComponent: 'span',
passedProps: {
dangerouslySetInnerHTML: {__html: sc('app_settings.payment.dont_have_paypal')},
},
},
{
powrComponent: 'a',
passedProps: {
className: 'link',
target: 'blank',
href: `${paypal_url}/webapps/merchantboarding/webflow/externalpartnerflow?partnerId=J4NURA49NDK6Q}`,
dangerouslySetInnerHTML: {__html: ' ' + sc('app_settings.payment.create_one_fast')},
},
},
],
},
{
powrComponent: PowrToggle,
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
passedProps: {
label: sc('app_settings.payment.allow_credit_card_payments_from_site'),
namespace: 'stripeEnabled',
addFormElementPadding: true,
helpText: sc('app_settings.payment.stripe_connect_help'),
handleChangeComplete: (value, observableStore) => {
if (!observableStore.model.meta.external_data || !observableStore.model.meta.external_data.stripe_connected) {
observableStore.model.connectToStripe();
} else {
observableStore.updateValue({
key: 'model.stripeEnabled',
value,
refreshSettings: true,
});
}
},
},
dynamicProps: [
{
propName: 'featurePremiumStatus',
evaluateWithStore: checkPaymentTypes('paypalEnabled', 'offlineEnabled')
},
],
},
{
powrComponent: PowrToggle,
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
passedProps: {
label: sc('app_settings.payment.allow_offline_payments'),
namespace: 'offlineEnabled',
addFormElementPadding: true,
helpText: sc('app_settings.payment.allow_offline_payments_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.offlineEnabled',
value,
refreshSettings: true,
});
},
},
dynamicProps: [
{
propName: 'featurePremiumStatus',
evaluateWithStore: checkPaymentTypes('paypalEnabled', 'stripeEnabled')
},
],
},
];
const productDetails = [
{
powrComponent: PowrMultitoggle,
passedProps: {
namespace: `${namespace}PurchaseType`,
label: sc('app_settings.payment.type_of_purchase'),
addFormElementPadding: true,
options: [['One time purchase', 'BuyNow'], ['Donation', 'Donate'], ['Subscription', 'Recurring']],
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: `model.${namespace}PurchaseType`,
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: PowrBannerNotification,
passedProps: {
type: 'caution',
label: sc('app_settings.payment.paypal_business_alert'),
addFormElementPadding: true,
},
conditionals: [
{
key: `observableStore.model.attributes.${namespace}PurchaseType`,
value: 'Recurring',
},
],
},
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.product_name'),
namespace: `${namespace}ItemName`,
addFormElementPadding: true,
placeholder: sc('app_settings.payment.product'),
validateLengthMax: 500,
},
},
{
powrComponent: PowrSelect,
passedProps: {
label: sc('app_settings.payment.currency'),
addFormElementPadding: true,
namespace: `${namespace}CurrencyCode`,
options: currencies,
},
},
{
powrComponent: 'div',
conditionals: [
{
key: `observableStore.model.attributes.${namespace}PurchaseType`,
value: 'Recurring',
},
],
innerComponents: subscriptionDetails,
},
{
powrComponent: 'div',
conditionals: [
{
key: `observableStore.model.attributes.${namespace}PurchaseType`,
check: currentVal => {
return currentVal !== 'Recurring';
},
},
],
innerComponents: nonSubscriptionDetails,
},
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.show_price_summary'),
namespace: 'showPriceSummary',
addFormElementPadding: true,
},
},
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.allow_note_to_seller'),
namespace: 'noteToSeller',
addFormElementPadding: true,
},
},
];
const taxDrilldown = [
{
powrComponent: PowrMultitoggle,
passedProps: {
namespace: `${namespace}TaxType`,
label: sc('app_settings.payment.tax_type'),
addFormElementPadding: true,
options: [
[sc('app_settings.payment.tax_rate'), 'rate'],
[sc('app_settings.payment.flat_tax'), 'flat'],
],
},
},
{
powrComponent: PowrTextInput,
conditionals: [
{
key: `observableStore.model.attributes.${namespace}TaxType`,
value: 'rate',
},
],
passedProps: {
label: sc('app_settings.payment.tax_rate'),
namespace: `${namespace}TaxRate`,
placeholder: sc('app_settings.payment.tax_rate_placeholder'),
validatePrice: true,
validateMax: 100,
addFormElementPadding: true,
},
},
{
powrComponent: PowrTextInput,
conditionals: [
{
key: `observableStore.model.attributes.${namespace}TaxType`,
value: 'flat',
},
],
passedProps: {
label: sc('app_settings.payment.tax_amount'),
namespace: `${namespace}TaxAmount`,
placeholder: sc('app_settings.payment.tax_amount_placeholder'),
validatePrice: true,
validateMax: 1000000,
addFormElementPadding: true,
},
},
];
const shippingDrilldown = [
{
powrComponent: PowrTextInput,
passedProps: {
label: sc('app_settings.payment.shipping_price'),
namespace: `${namespace}ShippingCost`,
placeholder: sc('app_settings.payment.enter_the_price_to_ship_one_item'),
validatePrice: true,
validateMax: 1000000,
addFormElementPadding: true,
helpText: sc('app_settings.payment.input_shipping_price'),
},
},
{
powrComponent: PowrMultitoggle,
passedProps: {
namespace: `${namespace}NoShipping`,
label: sc('app_settings.payment.address_prompt'),
addFormElementPadding: true,
options: [
[sc('app_settings.payment.prompt_for_an_address_but_do_not_require_one'), '0'],
[sc('app_settings.payment.do_not_prompt_for_an_address'), '1'],
[sc('app_settings.payment.prompt_for_an_address_and_require_one'), '2'],
],
},
}
];
const discountCodeDrilldown = [
{
powrComponent: PowrSettingsLabel,
passedProps: {
namespace: 'notifications',
label: sc('formbuilder.payment.disount_label'),
helpText: sc('formbuilder.payment.discount_label_help'),
addFormElementPadding: true,
},
},
{
powrComponent: PowrPrototypeSection,
passedProps: {
isFirstItemDeletable: true,
namespace: 'discounts',
onDelete: (observableStore, position) => {
const updated = observableStore.model.attributes.discounts.filter((v, i) => i !== position);
observableStore.updateValue({
key: 'model.discounts',
value: updated,
refreshSettings: true,
});
removeDiscountCodesFeature(observableStore);
},
onReorder: (observableStore, newOrder) => {
const updated = newOrder.map(newIndex => observableStore.model.attributes.discounts[newIndex]);
observableStore.updateValue({
key: 'model.discounts',
value: updated,
refreshSettings: true,
});
},
},
dynamicProps: [
{
propName: 'existingComponents',
key: 'observableStore.model.attributes.discounts',
evaluate: data => {
return _.map(data, (value, position) => {
return discountPrototype(position);
});
},
},
],
},
// {
// powrComponent: 'div',
// passedProps: {
// className: 'small-help text-center margin-top-m',
// },
// innerComponents: [
// {
// powrComponent: 'span',
// passedProps: {
// dangerouslySetInnerHTML: {__html: sc('app_settings.payment.discount_codes_case_sensitive')},
// },
// },
// ],
// ]
// },
{
powrComponent: PowrButton,
passedProps: {
label: sc('app_settings.payment.add_discount_code'),
namespace: 'view_form_responses',
addFormElementPadding: true,
buttonType: 'primary',
handleChangeComplete: (event, observableStore) => {
let discounts = [...observableStore.model.attributes.discounts];
const discountObj = {discountCode: '', discountType: 'flat', discountAmount: ''};
if (Array.isArray(discounts)) {
discounts.push(discountObj);
} else {
discounts = [discountObj];
}
observableStore.updateValue({
key: 'model.discounts',
value: discounts,
refreshSettings: true,
});
},
},
dynamicProps: [
{
propName: 'currentLimitsCount',
key: 'observableStore.model.attributes.discounts',
evaluate: currentVal => {
return currentVal.length;
},
},
{
propName: 'limits',
evaluate: () => {
if (isInABTest('ab_starter_removes_watermark')) {
return {
free: 0,
pro: 3,
enterprise: 10000,
};
}
return {
free: 0,
premium: 1,
pro: 3,
enterprise: 10000,
};
}
}
],
},
];
const formbuilderPayments = [
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.require_payment'),
namespace: 'paymentRequired',
addFormElementPadding: true,
helpText: sc('app_settings.payment.require_payment_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.paymentRequired',
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'connectPaymentAccounts',
label: sc('app_settings.payment.connect_payment_accounts'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'warningIcon',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
showPurpleDemoStar(connectPaymentAccounts)
],
innerComponents: connectPaymentAccounts
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'productDetails',
label: sc('app_settings.payment.product_details'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(productDetails),
],
innerComponents: productDetails,
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'taxDrilldown',
label: sc('app_settings.payment.tax'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(taxDrilldown)
],
innerComponents: taxDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'shippingDrilldown',
label: sc('app_settings.payment.shipping'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(shippingDrilldown)
],
innerComponents: shippingDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'discountCode',
label: sc('app_settings.payment.discount_code'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(discountCodeDrilldown)
],
innerComponents: discountCodeDrilldown
},
{
powrComponent: PowrButton,
passedProps: {
label: sc('app_settings.payment.preview_checkout'),
buttonType: 'primary',
namespace: 'previewCheckout',
handleChangeComplete: (e, observableStore) => {
window.checkout(e, true);
},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'disabled',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
{
powrComponent: 'div',
passedProps: {
className: 'small-warning text-center pad-top-nil',
dangerouslySetInnerHTML: {__html: `<i class='fa fa-warning failure'></i> ${sc('app_settings.payment.connect_payment_account')}`},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
{
key: 'observableStore.model.attributes',
check: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
];
export default formbuilderPayments;
- powrComponent - actual React component
- passedProps - react component's props (they are hardcoded and don't change based on user or app)
- dynamicProps - react component's props which are based on the model (for example if you need to pass an ID or createdAt).
- conditionals - array of conditions when to show this component, every expression must evaluate to true
- innerComponents - array of children components
Each item consists of:
const componentRenderer = require('@/../config/apps/sections_renderer').default;
componentRenderer(self, 'form_builder', [
{
component: 'content',
querySelector: '#powr-form-builder-content',
},
{
component: 'settings',
querySelector: '#powr-form-builder-settings',
},
{
component: 'payment',
querySelector: '#powr-form-builder-payment',
},
{
component: 'design',
querySelector: '#powr-form-builder-design',
},
{
component: 'import',
querySelector: '#powr-get-started',
},
]);Rendering sections for FormBuilder
export default (self, appName, configs) => {
const React = require('react');
const ReactDOM = require('react-dom');
const PowrSection = require('@/../modules/react_components/powr_section').default;
const ObservableStore = require('@/../helpers/observable_store').default;
let siteColors, siteColorsRef, appsDir = 'apps';
self.observableStore = new ObservableStore();
self.observableStore.model = self.model;
self.observableStore.experiments = window.GLOBALS;
if (HOST === 'wix') {
appsDir = 'wix-apps';
if(Wix) {
siteColorsRef = {};
const wixSiteColors = Wix.Styles.getSiteColors();
_.each(wixSiteColors, (color) => {
siteColorsRef[color['value'].toLowerCase()] = color['reference']
});
siteColors = _.pluck(wixSiteColors, 'value');
const recentColors = siteColors.splice(0,5);
try {
localStorage.setItem('recentColorsArr', JSON.stringify(recentColors));
} catch (e) {
debug()('localstorage is disabled');
}
Wix.Dashboard.getEditorUrl(function(url) {
self.$el.find('.openSite').attr('href', url);
});
self.observableStore.siteColorsRef = siteColorsRef;
self.observableStore.siteColors = _.chunk(siteColors.slice(5), 5);
Wix.Settings.getDashboardAppUrl(function(url) {
//Set the open dashboard url
let dashboardUrl;
if (url) {
dashboardUrl = url + '&powr_app_id=' + self.model.meta.id;
} else {
dashboardUrl = '/apps/' + self.model.meta.id + '/responses';
}
self.observableStore.updateValue({
key: 'dashboardUrl',
value: dashboardUrl
});
});
}
}
configs.forEach((config) => {
// wix does not have import tab
if (HOST === 'wix' && config.component === 'import') return;
let componentsList = require(`@/../config/${appsDir}/${appName}/${config.component}`).default;
const wrap =
self.observableStore &&
self.observableStore.model &&
self.observableStore.model.meta &&
appHasReactSections(self.observableStore.model.meta.app_namespace) &&
config.component === 'design';
if (wrap) {
componentsList = wrapToSmartDesign(componentsList);
}
const domNode = document.querySelector(`${config.querySelector}`);
if (domNode) {
ReactDOM.render(
<PowrSection componentsList={componentsList} observableStore={self.observableStore} />,
domNode
);
}
});
}@/../config/apps/sections_renderer.jsx
import React, {Fragment} from 'react';
import {observer} from 'mobx-react';
import {recursivelyBuildComponents} from '@/../helpers/react_component_builder';
@observer
class PowrSection extends React.Component {
render() {
return (
<Fragment>
<div className="hid">{this.props.observableStore.randomNumber}</div>
{recursivelyBuildComponents(this.props)}
</Fragment>
);
}
}
export default PowrSection;
@/../modules/react_components/powr_section.jsx
import React, {Fragment} from 'react';
import {saveUnblockedFeatures} from '@/../config/apps/form_builder/unblockedFeatures';
import {SMART_DESIGN_APPLIED, SMART_DESIGN_CHANGED} from '@./../config/partials/smart_design_wrapper';
export const recursivelyBuildComponents = ({componentsList, observableStore}, index = 0) => {
let shouldDisplay;
let counter = index * 10;
return _.map(componentsList, component => {
const {powrComponent, passedProps, innerComponents, conditionals, dynamicProps} = component;
shouldDisplay = true;
counter++;
//conditionals
if (conditionals) {
for (let i = 0; i < conditionals.length; i++) {
const condition = conditionals[i];
let currentVal;
if (condition.key) {
currentVal = getValue(observableStore, condition.key.replace(/observableStore(.)?/, ''));
}
if (typeof condition.check == 'function') {
shouldDisplay = condition.check(currentVal);
} else {
shouldDisplay = _.isEqual(currentVal, condition.value);
}
// Don't build the component if shouldn't display
// break out of the loop if false
if (!shouldDisplay) {
return false;
}
}
}
//dynamicProps
let calculatedProps = {};
if (dynamicProps) {
_.each(dynamicProps, condition => {
let currentVal;
if (condition.key) {
currentVal = getValue(observableStore, condition.key.replace(/observableStore(.)?/, ''));
}
calculatedProps[condition.propName] = currentVal;
if (typeof condition.evaluate == 'function') {
calculatedProps[condition.propName] = condition.evaluate(currentVal);
}
if (typeof condition.evaluateWithStore == 'function') {
calculatedProps[condition.propName] = condition.evaluateWithStore(observableStore);
}
});
}
let props = buildProps(observableStore, powrComponent, passedProps, calculatedProps, counter);
//innerComponents
let children;
if (innerComponents) {
children = recursivelyBuildComponents({componentsList: innerComponents, observableStore}, index++);
}
if (powrComponent === Fragment) {
props = _.pick(props, ['key']);
}
return React.createElement(powrComponent, props, children);
});
};
// This is helper built to get nested values from an object
/*****
eg object = { model: { attributes: { label: 'Abc',value: '123' } } , meta: {id: 123}}
getValue(object, 'model.attributes.label')
=>'Abc'
getValue(object, 'model.attributes')
=> { label: 'Abc',value: '123' }
getValue(object)
=> { model: { attributes: { label: 'Abc',value: '123' } } , meta: {id: 123}}
*****/
const getValue = (object, key) => {
if (_.isEmpty(key)) return object;
const keyList = key.split('.');
let result = object && object[keyList[0]];
for (let i = 1; i < keyList.length; i++) {
result = result && result[keyList[i]];
}
return result;
};
const checkPowrComponent = powrComponent => {
const componentName = powrComponent.name ||
powrComponent.toString().match(/^function\s*([^\s(]+)/) &&
powrComponent.toString().match(/^function\s*([^\s(]+)/)[1];;
const result = {
isPowrComponent: componentName && componentName.match(/powr/i),
componentName
}
return result;
}
const buildProps = (observableStore, powrComponent, passedProps, calculatedProps, counter) => {
const {isPowrComponent, componentName} = checkPowrComponent(powrComponent);
const userPremiumStatus = observableStore.model.meta.premium_status;
if (!isPowrComponent) {
return {
...passedProps,
...calculatedProps,
observableStore: observableStore,
key: counter,
};
}
// Unless the value is passed from the config grab it from the model.
if (!isValuePassedIn(passedProps) && !isValuePassedIn(calculatedProps)) {
const value = passedProps.namespace && observableStore.model.get(passedProps.namespace);
calculatedProps.value = value;
}
// powrComponents need premiumStatus and handleChangeComplete events unlike 'div', 'span', 'a' etc
return {
...passedProps,
...calculatedProps,
key: counter,
observableStore: observableStore,
userPremiumStatus: userPremiumStatus,
handleChangeComplete: (value, ...otherArguments) => {
if (passedProps.handleChangeComplete) {
passedProps.handleChangeComplete(value, observableStore, otherArguments);
} else {
if (passedProps.namespace) {
observableStore.updateValue({
key: `model.${passedProps.namespace}`,
value,
refreshSettings: true,
renderViewStyleOnly: passedProps.renderViewStyleOnly,
});
}
}
const smartDesignChanged =
observableStore.model.attributes.smartDesign === SMART_DESIGN_APPLIED &&
passedProps.namespace !== 'smartDesign' &&
value !== SMART_DESIGN_APPLIED;
if (smartDesignChanged) {
observableStore.updateValue({
key: 'model.smartDesign',
value: SMART_DESIGN_CHANGED,
});
}
observableStore.model.triggerChange(!passedProps.ignore);
saveUnblockedFeatures(observableStore, passedProps, calculatedProps, {userPremiumStatus, componentName, value});
},
};
};
const isValuePassedIn = props => {
return _.includes(_.keys(props), 'value');
};
@/../helpers/react_component_builder.jsx
Payment PowrSection

const formbuilderPayments = [
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.require_payment'),
namespace: 'paymentRequired',
addFormElementPadding: true,
helpText: sc('app_settings.payment.require_payment_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.paymentRequired',
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'connectPaymentAccounts',
label: sc('app_settings.payment.connect_payment_accounts'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'warningIcon',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
showPurpleDemoStar(connectPaymentAccounts)
],
innerComponents: connectPaymentAccounts
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'productDetails',
label: sc('app_settings.payment.product_details'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(productDetails),
],
innerComponents: productDetails,
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'taxDrilldown',
label: sc('app_settings.payment.tax'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(taxDrilldown)
],
innerComponents: taxDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'shippingDrilldown',
label: sc('app_settings.payment.shipping'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(shippingDrilldown)
],
innerComponents: shippingDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'discountCode',
label: sc('app_settings.payment.discount_code'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(discountCodeDrilldown)
],
innerComponents: discountCodeDrilldown
},
{
powrComponent: PowrButton,
passedProps: {
label: sc('app_settings.payment.preview_checkout'),
buttonType: 'primary',
namespace: 'previewCheckout',
handleChangeComplete: (e, observableStore) => {
window.checkout(e, true);
},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'disabled',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
{
powrComponent: 'div',
passedProps: {
className: 'small-warning text-center pad-top-nil',
dangerouslySetInnerHTML: {__html: `<i class='fa fa-warning failure'></i> ${sc('app_settings.payment.connect_payment_account')}`},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
{
key: 'observableStore.model.attributes',
check: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
];
export default formbuilderPayments;
Payment PowrSection


const formbuilderPayments = [
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.require_payment'),
namespace: 'paymentRequired',
addFormElementPadding: true,
helpText: sc('app_settings.payment.require_payment_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.paymentRequired',
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'connectPaymentAccounts',
label: sc('app_settings.payment.connect_payment_accounts'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'warningIcon',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
showPurpleDemoStar(connectPaymentAccounts)
],
innerComponents: connectPaymentAccounts
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'productDetails',
label: sc('app_settings.payment.product_details'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(productDetails),
],
innerComponents: productDetails,
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'taxDrilldown',
label: sc('app_settings.payment.tax'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(taxDrilldown)
],
innerComponents: taxDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'shippingDrilldown',
label: sc('app_settings.payment.shipping'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(shippingDrilldown)
],
innerComponents: shippingDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'discountCode',
label: sc('app_settings.payment.discount_code'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(discountCodeDrilldown)
],
innerComponents: discountCodeDrilldown
},
{
powrComponent: PowrButton,
passedProps: {
label: sc('app_settings.payment.preview_checkout'),
buttonType: 'primary',
namespace: 'previewCheckout',
handleChangeComplete: (e, observableStore) => {
window.checkout(e, true);
},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'disabled',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
{
powrComponent: 'div',
passedProps: {
className: 'small-warning text-center pad-top-nil',
dangerouslySetInnerHTML: {__html: `<i class='fa fa-warning failure'></i> ${sc('app_settings.payment.connect_payment_account')}`},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
{
key: 'observableStore.model.attributes',
check: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
];
export default formbuilderPayments;
Payment PowrSection



const formbuilderPayments = [
{
powrComponent: PowrToggle,
passedProps: {
label: sc('app_settings.payment.require_payment'),
namespace: 'paymentRequired',
addFormElementPadding: true,
helpText: sc('app_settings.payment.require_payment_help'),
handleChangeComplete: (value, observableStore) => {
observableStore.updateValue({
key: 'model.paymentRequired',
value,
refreshSettings: true,
});
},
},
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'connectPaymentAccounts',
label: sc('app_settings.payment.connect_payment_accounts'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'warningIcon',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
showPurpleDemoStar(connectPaymentAccounts)
],
innerComponents: connectPaymentAccounts
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'productDetails',
label: sc('app_settings.payment.product_details'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(productDetails),
],
innerComponents: productDetails,
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'taxDrilldown',
label: sc('app_settings.payment.tax'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(taxDrilldown)
],
innerComponents: taxDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'shippingDrilldown',
label: sc('app_settings.payment.shipping'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(shippingDrilldown)
],
innerComponents: shippingDrilldown
},
{
powrComponent: PowrDrilldown,
passedProps: {
namespace: 'discountCode',
label: sc('app_settings.payment.discount_code'),
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
showPurpleDemoStar(discountCodeDrilldown)
],
innerComponents: discountCodeDrilldown
},
{
powrComponent: PowrButton,
passedProps: {
label: sc('app_settings.payment.preview_checkout'),
buttonType: 'primary',
namespace: 'previewCheckout',
handleChangeComplete: (e, observableStore) => {
window.checkout(e, true);
},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
],
dynamicProps: [
{
propName: 'disabled',
key: 'observableStore.model.attributes',
evaluate: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
{
powrComponent: 'div',
passedProps: {
className: 'small-warning text-center pad-top-nil',
dangerouslySetInnerHTML: {__html: `<i class='fa fa-warning failure'></i> ${sc('app_settings.payment.connect_payment_account')}`},
},
conditionals: [
{
key: 'observableStore.model.attributes.paymentRequired',
value: true,
},
{
key: 'observableStore.model.attributes',
check: attributes => {
return !attributes.paypalPaypalAccount && !attributes.stripeEnabled && !attributes.offlineEnabled;
},
},
],
},
];
export default formbuilderPayments;

plugin

build



How to build your plugin
By Sergey Tyan
How to build your plugin
- 40