Skip to content

Commit

Permalink
feat: Add form-only donation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
Fishbakh-N committed Jul 11, 2024
1 parent 581f17f commit f022ddc
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 1 deletion.
130 changes: 130 additions & 0 deletions packages/donate-button-v4/src/formOnlyMode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {render} from 'preact';
import EmbedButton from 'src/components/embed-button';
import {CreateButtonInSelectorProps} from 'src/components/embed-button/types';
import {CreateWidgetInSelectorProps} from 'src/components/widget/types';
import {WidgetConfig} from 'src/components/widget/types/WidgetConfig';
import {loadFonts} from 'src/loadFonts';
import {FormLoader} from 'src/loaders/FormLoader';
import {WidgetLoader} from 'src/loaders/Widgetloader';
import resetcss from 'src/resetCss';

interface GlobalExport {
createButton: (options: CreateButtonInSelectorProps) => void;
createWidget: (options: CreateWidgetInSelectorProps) => void;
setOptions: (options: Partial<WidgetConfig>) => void;
showWidget: () => void;
}

declare const window: Window & {
everyDotOrgDonateButton?: GlobalExport;
};

export default function formOnlyMode() {
const DEFAULT_HASH_OPEN_WIDGET = 'donate';

const baseOptions: Partial<WidgetConfig> = {};
const options = {
show: false,
openAt: DEFAULT_HASH_OPEN_WIDGET
};
const instanceOptions: Partial<WidgetConfig> = {};

const getNode = (element?: Element, selector?: string) =>
element ? element : selector ? document.querySelector(selector) : null;

loadFonts();

/**
* Helper function to debug donate button issues
*/
function log(...messages: unknown[]): void {
console.info('Every.org Donate Button:', ...messages);
}

let formContainer: HTMLElement;
let formMountPoint: HTMLElement;

const mountWidget = () => {
const shadowWidgetWrapper = document.createElement('div');
shadowWidgetWrapper.id = 'shadow-wrapper';
formContainer.append(shadowWidgetWrapper);

formMountPoint = document.createElement('div');
shadowWidgetWrapper.attachShadow({mode: 'open'}).append(formMountPoint);

const everyStyles: HTMLStyleElement | null =
document.querySelector('#every-styles');

if (everyStyles) {
const rules = Object.values(everyStyles.sheet?.cssRules ?? {})
.map((rule) => rule.cssText)
.join('\n');

const everyShadowStyles = document.createElement('style');
everyShadowStyles.id = 'every-shadow-styles';
everyShadowStyles.innerHTML = resetcss + rules;

formMountPoint.append(everyShadowStyles);
}
};

const renderWidget = () => {
if (!formMountPoint) {
mountWidget();
}

const finalOptions: Partial<WidgetConfig> = {
...options,
...baseOptions,
...instanceOptions
};

render(<FormLoader options={finalOptions} />, formMountPoint);
};

function setOptions(newOptions: Partial<WidgetConfig>) {
Object.assign(baseOptions, newOptions);
renderWidget();
}

const createWidgetInSelector = ({
element,
selector,
...options
}: CreateWidgetInSelectorProps) => {
if (!element && !selector) {
log('createWidget():', 'must provide element or selector');
}

const node = getNode(element, selector);
if (!node) {
log('createWidget():', 'element or selector not found');
return;
}

if (!options.nonprofitSlug) {
log('createWidget():', 'must provide nonprofitSlug');
return;
}

formContainer = node as HTMLElement;

Object.assign(baseOptions, options);
renderWidget();
};

window.everyDotOrgDonateButton = {
createButton: () => {
log(
'createButton function is not available in formOnlyMode. Use createWidget instead.'
);
},
showWidget: () => {
log(
'showWidget function is not available in formOnlyMode. Use createWidget instead.'
);
},
setOptions,
createWidget: createWidgetInSelector
};
}
7 changes: 6 additions & 1 deletion packages/donate-button-v4/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import autoPlayMode from 'src/autoPlayMode';
import formOnlyMode from 'src/formOnlyMode';
import {shouldEnableAutoPlay} from 'src/helpers/shouldEnableAutoPlay';
import {shouldEnableFormOnlyMode} from 'src/helpers/souldEnableFormOnlyMode';
import manualMode from 'src/manualMode';

const autoPlay = shouldEnableAutoPlay();
const formOnly = shouldEnableFormOnlyMode();

if (autoPlay) {
if (formOnly) {
formOnlyMode();
} else if (autoPlay) {
autoPlayMode();
} else {
manualMode();
Expand Down
41 changes: 41 additions & 0 deletions packages/donate-button-v4/src/loaders/FormLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {PaymentProcess} from 'src/components/widget/components/PaymentProcess';
import {ContextProvider} from 'src/components/widget/context';
import {useConfigContext} from 'src/components/widget/hooks/useConfigContext';
import {useFundraiser} from 'src/components/widget/hooks/useFundraiser';
import {useNonprofit} from 'src/components/widget/hooks/useNonprofit';
import {LoadingIcon} from 'src/components/widget/icons/LoadingIcon';
import {FundraiserFetching} from 'src/components/widget/types/Fundraiser';
import {NonprofitFetching} from 'src/components/widget/types/Nonprofit';
import {WidgetConfig} from 'src/components/widget/types/WidgetConfig';

interface FormLoaderProps {
options: Partial<WidgetConfig>;
}

export const FormLoader = ({options = {}}: FormLoaderProps) => {
return (
<ContextProvider
options={options}
hide={() => {
// do nothing
}}
>
<From />
</ContextProvider>
);
};

const From = () => {
const {fundraiserSlug} = useConfigContext();
const findraiser = useFundraiser();
const nonprofit = useNonprofit();

if (
nonprofit === NonprofitFetching ||
(fundraiserSlug && findraiser === FundraiserFetching)
) {
return <LoadingIcon size={24} />;
}

return <PaymentProcess />;
};

0 comments on commit f022ddc

Please sign in to comment.