Skip to content

Commit

Permalink
Feature: Preview API (#7059)
Browse files Browse the repository at this point in the history
Co-authored-by: Ante Laca <[email protected]>
Co-authored-by: Jon Waldstein <[email protected]>
  • Loading branch information
3 people authored Nov 2, 2023
1 parent 2a40016 commit b6b2ff4
Show file tree
Hide file tree
Showing 22 changed files with 398 additions and 224 deletions.
5 changes: 3 additions & 2 deletions src/DonationForms/Controllers/DonationFormViewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ public function preview(DonationFormPreviewRouteData $data): string
$viewModel = new DonationFormViewModel(
$donationForm->id,
$data->formBlocks ?: $donationForm->blocks,
$data->formSettings ?: $donationForm->settings
$data->formSettings ?: $donationForm->settings,
true
);

ob_clean();
return $viewModel->render(true);
return $viewModel->render();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ public function toArray(): array
'currentAmount' => $currentAmount,
'targetAmount' => $this->targetAmount,
'label' => $this->getLabel(),
'progressPercentage' => $progressPercentage,
'isAchieved' => $this->isEnabled && $this->formSettings->enableAutoClose && $progressPercentage >= 100
];
}
Expand Down
17 changes: 12 additions & 5 deletions src/DonationForms/ViewModels/DonationFormViewModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,25 @@ class DonationFormViewModel
* @var DonationFormRepository
*/
private $donationFormRepository;
/**
* @var bool
*/
private $previewMode;

/**
* @since 3.0.0
*/
public function __construct(
int $donationFormId,
BlockCollection $formBlocks,
FormSettings $formSettings
FormSettings $formSettings,
bool $previewMode = false
) {
$this->donationFormId = $donationFormId;
$this->formBlocks = $formBlocks;
$this->formSettings = $formSettings;
$this->donationFormRepository = give(DonationFormRepository::class);
$this->previewMode = $previewMode;
}

/**
Expand Down Expand Up @@ -223,6 +229,7 @@ public function exports(): array
'isMultiStep' => $formDesign->isMultiStep(),
] : null,
]),
'previewMode' => $this->previewMode,
];
}

Expand All @@ -240,7 +247,7 @@ public function exports(): array
*
* @since 3.0.0
*/
public function render(bool $preview = false): string
public function render(): string
{
$this->enqueueGlobalStyles();

Expand All @@ -255,16 +262,16 @@ public function render(bool $preview = false): string
?>

<?php
if ($this->formSettings->customCss): ?>
<style><?php
if ($this->previewMode || $this->formSettings->customCss): ?>
<style id="root-givewp-donation-form-style"><?php
echo $this->formSettings->customCss; ?></style>
<?php
endif; ?>

<?php
$classNames = ['givewp-donation-form'];

if ($preview) {
if ($this->previewMode) {
$classNames[] = 'givewp-donation-form--preview';
}
?>
Expand Down
96 changes: 85 additions & 11 deletions src/DonationForms/resources/app/DonationFormApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import DonationFormErrorBoundary from '@givewp/forms/app/errors/boundaries/Donat
import MultiStepForm from '@givewp/forms/app/form/MultiStepForm';
import getDonationFormNodeSettings from '@givewp/forms/app/utilities/getDonationFormNodeSettings';
import {DonationFormSettingsProvider} from '@givewp/forms/app/store/form-settings';
import useDonationFormPubSub from '@givewp/forms/app/utilities/useDonationFormPubSub';
import {useEffect, useState} from 'react';
import type {Form as DonationForm} from '@givewp/forms/types';

const formTemplates = window.givewp.form.templates;
const GoalAchievedTemplate = withTemplateWrapper(formTemplates.layouts.goalAchieved);

/**
* Get data from the server
*/
const {form} = getWindowData();
const {form, previewMode} = getWindowData();
const donationFormNodeSettings = getDonationFormNodeSettings(form);

prepareFormData(form);

Expand All @@ -38,44 +42,114 @@ const initialState = {
validationSchema: schema,
};

const formSettings = {...form.settings, ...getDonationFormNodeSettings(form)};

/**
* @since 3.0.0
*/
function App() {
function App({form}: { form: DonationForm }) {

if (form.goal.isAchieved) {
return (
<DonationFormErrorBoundary>
<GoalAchievedTemplate goalAchievedMessage={form.settings.goalAchievedMessage}/>
<GoalAchievedTemplate goalAchievedMessage={form.settings.goalAchievedMessage} />
</DonationFormErrorBoundary>
);
}

if (form.design?.isMultiStep) {
return (
<DonationFormSettingsProvider value={formSettings}>
<DonationFormSettingsProvider value={{...form.settings, ...donationFormNodeSettings}}>
<DonationFormStateProvider initialState={initialState}>
<MultiStepForm sections={form.nodes} showHeader={form.settings?.showHeader} />
<MultiStepForm form={form} />
</DonationFormStateProvider>
</DonationFormSettingsProvider>
);
}

return (
<DonationFormSettingsProvider value={formSettings}>
<DonationFormSettingsProvider value={{...form.settings, ...donationFormNodeSettings}}>
<DonationFormStateProvider initialState={initialState}>
{form.settings?.showHeader && <Header />}
{form.settings?.showHeader && <Header form={form} />}
<Form defaultValues={defaultValues} sections={form.nodes} validationSchema={schema} />
</DonationFormStateProvider>
</DonationFormSettingsProvider>
);
}

/**
* @unreleased
*/
function AppPreview() {

const {
subscribeToGoal,
subscribeToColors,
subscribeToSettings,
subscribeToCss,
unsubscribeAll
} = useDonationFormPubSub();

const [formState, setFormState] = useState<DonationForm>(form);

useEffect(() => {
subscribeToSettings((settings) => {
setFormState(prevState => {
return {
...prevState,
settings: {
...prevState.settings,
...settings
}
}
})
})

subscribeToGoal((goal) => {
setFormState(prevState => {
return {
...prevState,
goal: {
...prevState.goal,
...goal
}
}
})
})

subscribeToColors((data) => {
if (data['primaryColor']) {
root.style.setProperty('--givewp-primary-color', data['primaryColor']);
}

if (data['secondaryColor']) {
root.style.setProperty('--givewp-secondary-color', data['secondaryColor']);
}
})

subscribeToCss(({customCss}) => {
let cssRules = '';
const stylesheet = new CSSStyleSheet();

stylesheet.replaceSync(customCss);

for (let i = 0; i < stylesheet.cssRules.length; i++) {
cssRules += stylesheet.cssRules[i].cssText + "\n";
}

style.innerText = cssRules;
})

return () => unsubscribeAll();

}, []);

return <App form={formState} />
}

const root = document.getElementById('root-givewp-donation-form');
const style = document.getElementById('root-givewp-donation-form-style');

if (createRoot) {
createRoot(root).render(<App />);
createRoot(root).render(previewMode ? <AppPreview /> : <App form={form} />);
} else {
render(<App />, root);
render(previewMode ? <AppPreview /> : <App form={form} />, root);
}
23 changes: 11 additions & 12 deletions src/DonationForms/resources/app/form/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import {useCallback} from 'react';
import {withTemplateWrapper} from '../templates';
import getWindowData from '../utilities/getWindowData';
import type {GoalType} from '@givewp/forms/propTypes';
import amountFormatter from '@givewp/forms/app/utilities/amountFormatter';
import DonationFormErrorBoundary from '@givewp/forms/app/errors/boundaries/DonationFormErrorBoundary';

const {form} = getWindowData();
const formTemplates = window.givewp.form.templates;

const HeaderTemplate = withTemplateWrapper(formTemplates.layouts.header);
const HeaderTitleTemplate = withTemplateWrapper(formTemplates.layouts.headerTitle);
const HeaderDescriptionTemplate = withTemplateWrapper(formTemplates.layouts.headerDescription);
const GoalTemplate = withTemplateWrapper(formTemplates.layouts.goal);

/**
* @since 3.0.0
*/
const formatGoalAmount = (amount: number) => {
return amountFormatter(form.currency, {
maximumFractionDigits: 0,
}).format(amount);
};


/**
* @since 3.0.0
*/
export default function Header() {
export default function Header({form}) {

const formatGoalAmount = useCallback((amount: number) => {
return amountFormatter(form.currency, {
maximumFractionDigits: 0,
}).format(amount);
}, []);

return (
<DonationFormErrorBoundary>
<HeaderTemplate
Expand All @@ -38,7 +37,7 @@ export default function Header() {
currency={form.currency}
type={form.goal.type as GoalType}
goalLabel={form.goal.label}
progressPercentage={form.goal.progressPercentage}
progressPercentage={Math.round((form.goal.currentAmount / form.goal.targetAmount) * 100)}
currentAmount={form.goal.currentAmount}
currentAmountFormatted={
form.goal.typeIsMoney
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {useDonationFormSettings} from '@givewp/forms/app/store/form-settings';
/**
* @since 3.0.0
*/
export default function HeaderStep() {
export default function HeaderStep({form}) {
const dispatchMultiStep = useDonationFormMultiStepStateDispatch();
const {multiStepFirstButtonText} = useDonationFormSettings();

return (
<div>
<Header />
<Header form={form} />
<button
type="button"
className="givewp-donation-form__steps-button-next"
Expand Down
11 changes: 6 additions & 5 deletions src/DonationForms/resources/app/form/MultiStepForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import SectionNode from '@givewp/forms/app/fields/SectionNode';
import Steps from '@givewp/forms/app/form/MultiStepForm/components/Steps';
import HeaderStep from '@givewp/forms/app/form/MultiStepForm/components/HeaderStep';
import {DonationSummaryProvider} from '@givewp/forms/app/store/donation-summary';
import type {Form} from '@givewp/forms/types';

const FormSectionTemplate = withTemplateWrapper(window.givewp.form.templates.layouts.section, 'section');

Expand Down Expand Up @@ -55,23 +56,23 @@ const convertSectionsToSteps = (sections: Section[], hasFirstStep: boolean) => {
/**
* @since 3.0.0
*/
export default function MultiStepForm({sections, showHeader}: {sections: Section[]; showHeader?: boolean}) {
const steps = convertSectionsToSteps(sections, showHeader);
export default function MultiStepForm({form}: {form: Form}) {
const steps = convertSectionsToSteps(form.nodes, form.settings?.showHeader);

if (showHeader) {
if (form.settings?.showHeader) {
steps.unshift({
id: 0,
title: null,
description: null,
element: <HeaderStep />,
element: <HeaderStep form={form} />,
fields: [],
visibilityConditions: [],
isVisible: true,
});
}

return (
<DonationFormMultiStepStateProvider initialState={{steps, currentStep: 0, showHeader}}>
<DonationFormMultiStepStateProvider initialState={{steps, currentStep: 0, showHeader: form.settings?.showHeader}}>
<DonationSummaryProvider>
<Steps steps={steps} />
</DonationSummaryProvider>
Expand Down
Loading

0 comments on commit b6b2ff4

Please sign in to comment.