Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Preview API #7059

Merged
merged 54 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
5db7ef1
initial
Oct 11, 2023
592c37f
Merge remote-tracking branch 'origin/develop' into feature/preview-api
alaca Oct 19, 2023
6e23a61
fix: props
alaca Oct 19, 2023
2719a5b
refactor: state
alaca Oct 20, 2023
5976369
refactor: pass state as props
alaca Oct 20, 2023
2c0f65e
feature: dispatchSettings
alaca Oct 20, 2023
25b5e15
feature: useIframeMessages
alaca Oct 20, 2023
5e4c295
wip
alaca Oct 20, 2023
91347d7
refactor: fetch on designId change
alaca Oct 20, 2023
9c58541
feature: colors settings
alaca Oct 20, 2023
d3b9a39
feature: donate button caption
alaca Oct 20, 2023
0f924fc
refactor: unify form settings
alaca Oct 20, 2023
8befba9
refactor: rename file
alaca Oct 23, 2023
f1047f3
refactor: pass `form` as prop
alaca Oct 23, 2023
0852fb1
feature: add iframe ref
alaca Oct 23, 2023
32cd68f
wip
alaca Oct 23, 2023
ba216b0
wip
alaca Oct 23, 2023
4eebc27
feature: use pubsub
alaca Oct 23, 2023
8728a12
Merge remote-tracking branch 'origin/develop' into feature/preview-api
alaca Oct 23, 2023
f07721f
chore: cleanup
alaca Oct 23, 2023
8ba1974
refactor: remove debounce and unused components
alaca Oct 23, 2023
9d6e5aa
refactor: extract pub/sub functionality into separate component
alaca Oct 24, 2023
e7b4df0
refactor: wrap function in useCallback
alaca Oct 24, 2023
a66f19b
feature: add Form type
alaca Oct 24, 2023
c5ddb52
refactor: remove debounce; use new functions publishSettings and publ…
alaca Oct 24, 2023
425932b
refactor: use new functions publishGoal
alaca Oct 24, 2023
8be8599
feature: add new types FormGoal, FormColors and RequireAtLeastOne
alaca Oct 24, 2023
fdc12ce
feature: add new functions for handling different parts of state
alaca Oct 24, 2023
7d311f2
chore: add spaces
alaca Oct 24, 2023
03310de
fix: donate button
alaca Oct 25, 2023
18def4b
fix: show header
alaca Oct 25, 2023
0789307
refactor: add publishGoalType function
alaca Oct 25, 2023
5398b06
feature: allow only primitive values
Oct 25, 2023
238ade6
feature: add preview prop to form state
Oct 25, 2023
d2c73ee
Merge branch 'develop' into feature/preview-api
Oct 25, 2023
2cd858c
refactor: filter messages
Oct 25, 2023
11a9d5f
feature: add preview for currency goal amount
Oct 26, 2023
e93f6c6
refactor: move previewMode into view model
Oct 26, 2023
3156b8f
refactor: remove debounce
Oct 26, 2023
e5b933c
tests: update view model test
Oct 26, 2023
8e79a0e
feature: add custom css preview
Oct 26, 2023
5b0f724
Merge remote-tracking branch 'origin/feature/preview-api' into featur…
Oct 26, 2023
afae56b
refactor: custom css preview
Oct 26, 2023
44b62f7
fix: primary color
Oct 26, 2023
63b31f4
Merge branch 'develop' into feature/preview-api
alaca Oct 26, 2023
1207b5e
refactor: use innerText instead of innerHTML
Oct 26, 2023
4d7c01e
Merge remote-tracking branch 'origin/feature/preview-api' into featur…
Oct 26, 2023
c38a489
refactor: calculate percentage on frontend
Oct 26, 2023
a22df14
refactor: remove unused prop
Oct 26, 2023
dd11979
refactor: css handling
Oct 26, 2023
406bff4
refactor: remove progressPercentage
Oct 27, 2023
0fcfe6b
refactor: get correct type
Oct 27, 2023
d213058
fix: reload design preview when goal type changes so that displayed v…
Oct 27, 2023
5b30de3
fix: disable worker
Nov 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading