mirror of
https://github.com/jlengrand/adyen-web.git
synced 2026-03-10 08:01:22 +00:00
Adding Storybook as dev playground (#1934)
* feat: draft * feat: cleaned up * feat: converted some files to ts * fix: redirect result + typescript * feat: cleaned up server * feat: adding some stories * feat: more changes * feat: small adjustments * feat: removed docs. cleaned preview * feat: cleanup * feat: clean up types * feat: adding to window object * feat: global loaders * feat: added type to loaded checkout * feat: storybook 7 + vite * feat: cleanup deps not used by vite or storybook * feat: removing unused import * Update main.ts * feat: webpack5 * feat: attempt to update packages * refactor: move storybook to lib * refactor: use rollup.dev.config.js * refactor: redirect story fix and add a11y check * refactor: rename story * refactor: split rollup config * refactor(storybook-config): use dev rollup config * remove playground-storybook folder * some fix * run storybook https * rebase main resolve conflicts * add mirrored rollup dev plugins to vite * correct postcss.config.js path * fix returnUrl * refactor: remove unused code and add types * feat: cleaning up --------- Co-authored-by: Yu Long <longyu901009@gmail.com>
This commit is contained in:
committed by
GitHub
parent
f5af3e1f5f
commit
f189f4eede
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
node_modules
|
||||
dist
|
||||
coverage
|
||||
storybook-static
|
||||
|
||||
*.log*
|
||||
.env
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"start": "concurrently --kill-others-on-fail \"yarn workspace @adyen/adyen-web start\" \"yarn workspace @adyen/adyen-web-playground start\" --names \"lib,playground\"",
|
||||
"start:storybook": "yarn workspace @adyen/adyen-web start:storybook",
|
||||
"build": "yarn workspace @adyen/adyen-web build",
|
||||
"format": "yarn workspace @adyen/adyen-web format",
|
||||
"lint": "yarn workspace @adyen/adyen-web lint",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
dist/
|
||||
server/
|
||||
config/
|
||||
src/polyfills.ts
|
||||
src/polyfills.ts
|
||||
!.storybook
|
||||
@@ -5,7 +5,8 @@ module.exports = {
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended' /*'prettier/@typescript-eslint'*/
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:storybook/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
@@ -41,8 +42,10 @@ module.exports = {
|
||||
}
|
||||
],
|
||||
'no-console': 0,
|
||||
'class-methods-use-this': 'off', // TODO
|
||||
'no-underscore-dangle': 'off', // TODO
|
||||
'class-methods-use-this': 'off',
|
||||
// TODO
|
||||
'no-underscore-dangle': 'off',
|
||||
// TODO
|
||||
'import/prefer-default-export': 'off',
|
||||
'no-debugger': 'warn',
|
||||
indent: 'off',
|
||||
@@ -56,13 +59,19 @@ module.exports = {
|
||||
tsx: 'never'
|
||||
}
|
||||
],
|
||||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: true
|
||||
}
|
||||
],
|
||||
'max-len': [
|
||||
'error',
|
||||
{
|
||||
code: 150,
|
||||
tabWidth: 2,
|
||||
ignoreComments: true, // Allow long comments in the code
|
||||
ignoreComments: true,
|
||||
// Allow long comments in the code
|
||||
ignoreUrls: true,
|
||||
ignoreStrings: true,
|
||||
ignoreTemplateLiterals: true
|
||||
@@ -86,20 +95,36 @@ module.exports = {
|
||||
radix: 'off',
|
||||
// This serves no practical purpose
|
||||
'eol-last': 'off',
|
||||
|
||||
// the base rule can report incorrect errors
|
||||
'no-useless-constructor': 'off',
|
||||
|
||||
// Typescript Rules
|
||||
'@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true, vars: 'local' }],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
ignoreRestSiblings: true,
|
||||
vars: 'local'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/explicit-member-accessibility': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/indent': 'off',
|
||||
'@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],
|
||||
'@typescript-eslint/ban-types': 'off', // TODO
|
||||
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': 'allow-with-description' }],
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off', // TODO
|
||||
'@typescript-eslint/no-empty-function': [
|
||||
'error',
|
||||
{
|
||||
allow: ['arrowFunctions']
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
// TODO
|
||||
'@typescript-eslint/ban-ts-comment': [
|
||||
'error',
|
||||
{
|
||||
'ts-ignore': 'allow-with-description'
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
// TODO
|
||||
|
||||
// React Rules
|
||||
'react/prop-types': 'off',
|
||||
@@ -107,7 +132,6 @@ module.exports = {
|
||||
|
||||
// TSDoc
|
||||
'tsdoc/syntax': 'warn',
|
||||
|
||||
// a11y
|
||||
'jsx-a11y/alt-text': 'error',
|
||||
'jsx-a11y/aria-role': 'error',
|
||||
@@ -127,11 +151,25 @@ module.exports = {
|
||||
'jsx-a11y/mouse-events-have-key-events': 'error'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['storybook/**/*.tsx'],
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
// enable the rule specifically for TypeScript files
|
||||
files: ['*.ts', '*.tsx'],
|
||||
rules: {
|
||||
'@typescript-eslint/explicit-member-accessibility': ['error', { accessibility: 'off', overrides: { properties: 'explicit' } }]
|
||||
'@typescript-eslint/explicit-member-accessibility': [
|
||||
'error',
|
||||
{
|
||||
accessibility: 'off',
|
||||
overrides: {
|
||||
properties: 'explicit'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
3
packages/lib/.gitignore
vendored
3
packages/lib/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
dist
|
||||
/*.tgz
|
||||
/*.tgz
|
||||
.stylelintcache
|
||||
14
packages/lib/.storybook/main.css
Normal file
14
packages/lib/.storybook/main.css
Normal file
@@ -0,0 +1,14 @@
|
||||
html,
|
||||
body {
|
||||
font: 16px/1.21 -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
font-weight: 400;
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
.component-wrapper {
|
||||
background: #fff;
|
||||
border: 1px solid #edf0f3;
|
||||
max-width: 600px;
|
||||
padding: 24px;
|
||||
margin: auto;
|
||||
}
|
||||
59
packages/lib/.storybook/main.ts
Normal file
59
packages/lib/.storybook/main.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { StorybookConfig } from '@storybook/preact-vite';
|
||||
import { mergeConfig, loadEnv } from 'vite';
|
||||
import * as path from 'path';
|
||||
import version = require('../config/version');
|
||||
import eslint from '@rollup/plugin-eslint';
|
||||
import stylelint from 'vite-plugin-stylelint';
|
||||
const currentVersion = version();
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../storybook/**/*.stories.mdx', '../storybook/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
addons: [
|
||||
{
|
||||
name: '@storybook/addon-essentials',
|
||||
options: {
|
||||
docs: false
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '@storybook/addon-a11y'
|
||||
}
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/preact-vite',
|
||||
options: {}
|
||||
},
|
||||
async viteFinal(config, options) {
|
||||
const env = loadEnv(options.configType, path.resolve('../../', '.env'), '');
|
||||
|
||||
return mergeConfig(config, {
|
||||
define: {
|
||||
'process.env': env,
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.VERSION': JSON.stringify(currentVersion.ADYEN_WEB_VERSION),
|
||||
'process.env.COMMIT_HASH': JSON.stringify(currentVersion.COMMIT_HASH),
|
||||
'process.env.COMMIT_BRANCH': JSON.stringify(currentVersion.COMMIT_BRANCH),
|
||||
'process.env.ADYEN_BUILD_ID': JSON.stringify(currentVersion.ADYEN_BUILD_ID),
|
||||
'process.env.__SF_ENV__': JSON.stringify(env.SF_ENV || 'build')
|
||||
},
|
||||
server: {
|
||||
watch: {
|
||||
usePolling: true
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
stylelint(),
|
||||
{
|
||||
...eslint({
|
||||
include: ['./src/**'],
|
||||
exclude: ['./src/**/*.json', './src/**/*.scss']
|
||||
}),
|
||||
enforce: 'pre',
|
||||
apply: 'serve'
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
9
packages/lib/.storybook/manager.ts
Normal file
9
packages/lib/.storybook/manager.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { addons } from '@storybook/manager-api';
|
||||
|
||||
/**
|
||||
* https://storybook.js.org/docs/html/configure/features-and-behavior
|
||||
*/
|
||||
addons.setConfig({
|
||||
panelPosition: 'right'
|
||||
});
|
||||
7
packages/lib/.storybook/middleware.js
Normal file
7
packages/lib/.storybook/middleware.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const checkoutDevServer = require('@adyen/adyen-web-server');
|
||||
|
||||
const middleware = router => {
|
||||
checkoutDevServer(router);
|
||||
};
|
||||
|
||||
module.exports = middleware;
|
||||
43
packages/lib/.storybook/preview.tsx
Normal file
43
packages/lib/.storybook/preview.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import './main.css';
|
||||
import '../src/style/index.scss';
|
||||
import { Preview } from '@storybook/preact';
|
||||
import { DEFAULT_COUNTRY_CODE, DEFAULT_SHOPPER_LOCALE, DEFAULT_AMOUNT_VALUE } from '../storybook/config/commonConfig';
|
||||
import { createCheckout } from '../storybook/helpers/create-checkout';
|
||||
|
||||
const preview: Preview = {
|
||||
argTypes: {
|
||||
useSessions: {
|
||||
control: 'boolean'
|
||||
},
|
||||
countryCode: {
|
||||
control: 'text'
|
||||
},
|
||||
shopperLocale: {
|
||||
control: 'text'
|
||||
},
|
||||
amount: {
|
||||
control: 'number'
|
||||
},
|
||||
showPayButton: {
|
||||
control: 'boolean'
|
||||
}
|
||||
},
|
||||
args: {
|
||||
useSessions: true,
|
||||
countryCode: DEFAULT_COUNTRY_CODE,
|
||||
shopperLocale: DEFAULT_SHOPPER_LOCALE,
|
||||
amount: DEFAULT_AMOUNT_VALUE,
|
||||
showPayButton: true
|
||||
},
|
||||
loaders: [
|
||||
async context => {
|
||||
if (context.componentId.includes('redirectresult')) {
|
||||
return {};
|
||||
}
|
||||
const checkout = await createCheckout(context);
|
||||
return { checkout };
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default preview;
|
||||
13
packages/lib/.storybook/run.js
Normal file
13
packages/lib/.storybook/run.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { execSync } = require('child_process');
|
||||
const path = require('path');
|
||||
require('dotenv').config({ path: path.resolve('../../', '.env') });
|
||||
|
||||
const isHttps = process.env.IS_HTTPS === 'true';
|
||||
const certPath = process.env.CERT_PATH ?? path.resolve(__dirname, 'localhost.pem');
|
||||
const certKeyPath = process.env.CERT_KEY_PATH ?? path.resolve(__dirname, 'localhost-key.pem');
|
||||
|
||||
const runStorybook = 'storybook dev --port 3020 --disable-telemetry';
|
||||
const runStorybookHttps = `storybook dev --port 3020 --https --ssl-cert ${certPath} --ssl-key ${certKeyPath} --disable-telemetry`;
|
||||
|
||||
execSync(isHttps ? runStorybookHttps : runStorybook, { stdio: 'inherit' });
|
||||
@@ -71,7 +71,7 @@ export async function getPlugins(analyze = isBundleAnalyzer) {
|
||||
postcss({
|
||||
use: ['sass'],
|
||||
config: {
|
||||
path: 'config/postcss.config.js'
|
||||
path: 'postcss.config.js'
|
||||
},
|
||||
sourceMap: true,
|
||||
inject: false,
|
||||
|
||||
@@ -34,9 +34,11 @@
|
||||
"scripts": {
|
||||
"start": "npm run dev-server",
|
||||
"dev-server": "cross-env NODE_ENV=development rollup --watch --config config/rollup.dev.config.js",
|
||||
"start:storybook": "node .storybook/run.js",
|
||||
"docs:generate": "typedoc --out docs src --exclude \"**/*+(index|.test).ts\"",
|
||||
"build": "rm -rf dist/ && npm run type-check-generate && cross-env NODE_ENV=production rollup --config config/rollup.config.js",
|
||||
"build:analyze": "rm -rf dist/ && cross-env NODE_ENV=analyze rollup --config config/rollup.config.js",
|
||||
"build:storybook": "storybook build --disable-telemetry",
|
||||
"test": "jest --config config/jest.config.js",
|
||||
"test:watch": "npm run test -- --watchAll",
|
||||
"test:coverage": "npm run test -- --coverage",
|
||||
@@ -54,12 +56,13 @@
|
||||
"prepare": "cd ../.. && husky install packages/lib/.husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@adyen/adyen-web-server": "1.0.0",
|
||||
"@babel/cli": "^7.18.10",
|
||||
"@babel/core": "^7.16.0",
|
||||
"@babel/plugin-transform-runtime": "^7.19.1",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-env": "^7.21.5",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/preset-typescript": "^7.21.5",
|
||||
"@babel/runtime-corejs3": "^7.20.1",
|
||||
"@rollup/plugin-babel": "^6.0.2",
|
||||
"@rollup/plugin-commonjs": "^24.0.0",
|
||||
@@ -67,6 +70,11 @@
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.0",
|
||||
"@rollup/plugin-replace": "^5.0.1",
|
||||
"@storybook/addon-a11y": "^7.0.20",
|
||||
"@storybook/addon-essentials": "^7.0.20",
|
||||
"@storybook/manager-api": "^7.0.20",
|
||||
"@storybook/preact": "^7.0.20",
|
||||
"@storybook/preact-vite": "^7.0.20",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/preact": "3.2.3",
|
||||
"@testing-library/preact-hooks": "1.1.0",
|
||||
@@ -85,6 +93,7 @@
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"eslint-plugin-react": "^7.31.8",
|
||||
"eslint-plugin-storybook": "^0.6.12",
|
||||
"eslint-plugin-testing-library": "^5.9.1",
|
||||
"eslint-plugin-tsdoc": "^0.2.17",
|
||||
"filesize": "^10.0.0",
|
||||
@@ -95,16 +104,21 @@
|
||||
"jest-mock-extended": "^3.0.1",
|
||||
"lint-staged": "^13.0.3",
|
||||
"postcss": "8.4.23",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"rollup": "^2.79.1",
|
||||
"rollup-plugin-postcss": "4.0.2",
|
||||
"rollup-plugin-stylelint": "1.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"sass": "1.62.1",
|
||||
"storybook": "^7.0.20",
|
||||
"stylelint": "15.10.1",
|
||||
"stylelint-config-standard-scss": "7.0.1",
|
||||
"tslib": "^2.4.1",
|
||||
"typescript": "4.9.5",
|
||||
"vite": "4.3.9",
|
||||
"vite-plugin-stylelint": "^4.3.0",
|
||||
"whatwg-fetch": "^3.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
5
packages/lib/postcss.config.js
Normal file
5
packages/lib/postcss.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
console.log('Using postcss plugins...');
|
||||
module.exports = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
plugins: [require('autoprefixer'), require('cssnano')({ preset: ['default', { colormin: false }] })]
|
||||
};
|
||||
6
packages/lib/storybook/config/commonConfig.ts
Normal file
6
packages/lib/storybook/config/commonConfig.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
const protocol = process.env.IS_HTTPS === 'true' ? 'https' : 'http';
|
||||
export const DEFAULT_SHOPPER_LOCALE = 'en-US';
|
||||
export const DEFAULT_COUNTRY_CODE = 'US';
|
||||
export const DEFAULT_AMOUNT_VALUE = 25900;
|
||||
export const SHOPPER_REFERENCE = 'newshoppert';
|
||||
export const RETURN_URL = `${protocol}://localhost:3020/?path=/story/helpers-redirectresult--redirect-result`;
|
||||
30
packages/lib/storybook/config/paymentMethodsConfig.ts
Normal file
30
packages/lib/storybook/config/paymentMethodsConfig.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { SHOPPER_REFERENCE } from './commonConfig';
|
||||
|
||||
const paymentMethodsConfig = {
|
||||
channel: 'Web',
|
||||
shopperReference: SHOPPER_REFERENCE,
|
||||
shopperName: {
|
||||
firstName: 'Jan',
|
||||
lastName: 'Jansen',
|
||||
gender: 'MALE'
|
||||
},
|
||||
telephoneNumber: '0612345678',
|
||||
shopperEmail: 'test@adyen.com',
|
||||
dateOfBirth: '1970-07-10'
|
||||
// billingAddress: {
|
||||
// city: 'Gravenhage',
|
||||
// country: commonConfiguration.countryCode,
|
||||
// houseNumberOrName: '1',
|
||||
// postalCode: '2521VA',
|
||||
// street: 'Neherkade'
|
||||
// },
|
||||
// deliveryAddress: {
|
||||
// city: 'Gravenhage',
|
||||
// country: commonConfiguration.countryCode,
|
||||
// houseNumberOrName: '2',
|
||||
// postalCode: '2521VA',
|
||||
// street: 'Neherkade'
|
||||
// }
|
||||
};
|
||||
|
||||
export default paymentMethodsConfig;
|
||||
49
packages/lib/storybook/config/paymentsConfig.ts
Normal file
49
packages/lib/storybook/config/paymentsConfig.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
const identifier = new Date().getMilliseconds();
|
||||
const protocol = process.env.IS_HTTPS === 'true' ? 'https' : 'http';
|
||||
|
||||
const { origin = `${protocol}://localhost:3020`, search } = window.location;
|
||||
const returnUrl = origin + search;
|
||||
|
||||
const paymentsConfig = {
|
||||
origin,
|
||||
returnUrl,
|
||||
reference: `${identifier}-checkout-components-ref`,
|
||||
additionalData: {
|
||||
// Force response code. See https://docs.adyen.com/development-resources/test-cards/result-code-testing/adyen-response-codes
|
||||
// RequestedTestAcquirerResponseCode: 2,
|
||||
allow3DS2: true
|
||||
},
|
||||
shopperEmail: 'test-shopper@storytel.com',
|
||||
shopperIP: '172.30.0.1',
|
||||
// threeDS2RequestData: {
|
||||
// authenticationOnly: false
|
||||
// },
|
||||
channel: 'Web',
|
||||
browserInfo: {
|
||||
acceptHeader: 'http'
|
||||
},
|
||||
lineItems: [
|
||||
{
|
||||
taxPercentage: 0,
|
||||
id: 'item1',
|
||||
taxAmount: 0,
|
||||
description: 'Test Item 1',
|
||||
amountIncludingTax: 75900,
|
||||
quantity: 1,
|
||||
taxCategory: 'None',
|
||||
amountExcludingTax: 75900
|
||||
},
|
||||
{
|
||||
taxPercentage: 0,
|
||||
id: 'item2',
|
||||
taxAmount: 0,
|
||||
description: 'Test Item 2',
|
||||
amountIncludingTax: 10000,
|
||||
quantity: 1,
|
||||
taxCategory: 'None',
|
||||
amountExcludingTax: 10000
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default paymentsConfig;
|
||||
19
packages/lib/storybook/helpers/Result.tsx
Normal file
19
packages/lib/storybook/helpers/Result.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
interface IResult {
|
||||
resultCode?: string;
|
||||
resultMessage?: string;
|
||||
}
|
||||
|
||||
export const Result = ({ resultCode, resultMessage }: IResult) => {
|
||||
const isAuthorized = resultCode === 'Authorised' || resultCode === 'Received';
|
||||
const imgSrc = isAuthorized
|
||||
? 'https://checkoutshopper-test.adyen.com/checkoutshopper/images/components/success.gif'
|
||||
: 'https://checkoutshopper-test.adyen.com/checkoutshopper/images/components/error.gif';
|
||||
const imgAlt = isAuthorized ? 'success' : 'error';
|
||||
|
||||
return (
|
||||
<div className="redirect-result">
|
||||
<img src={imgSrc} alt={imgAlt} className="result-img"></img>
|
||||
{resultCode} {resultMessage}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
39
packages/lib/storybook/helpers/checkout-api-calls.ts
Normal file
39
packages/lib/storybook/helpers/checkout-api-calls.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import paymentMethodsConfig from '../config/paymentMethodsConfig';
|
||||
import paymentsConfig from '../config/paymentsConfig';
|
||||
import { httpPost } from '../utils/http-post';
|
||||
import { PaymentMethodsResponse } from '../../src/core/ProcessResponse/PaymentMethodsResponse/types';
|
||||
import { RawPaymentResponse } from '../../src/components/types';
|
||||
import { CheckoutSessionSetupResponse, Order, OrderStatus, PaymentAction, PaymentAmount } from '../../src/types';
|
||||
|
||||
export const getPaymentMethods = async (configuration?: any): Promise<PaymentMethodsResponse> =>
|
||||
await httpPost('paymentMethods', { ...paymentMethodsConfig, ...configuration });
|
||||
|
||||
export const makePayment = async (stateData: any, paymentData: any): Promise<RawPaymentResponse> => {
|
||||
const paymentRequest = { ...paymentsConfig, ...stateData, ...paymentData };
|
||||
if (paymentRequest.order) delete paymentRequest.amount; // why?
|
||||
return await httpPost('payments', paymentRequest);
|
||||
};
|
||||
|
||||
export const makeDetailsCall = async (detailsData: {
|
||||
details: { redirectResult: string };
|
||||
}): Promise<{ resultCode: string; action?: PaymentAction }> => await httpPost('details', detailsData);
|
||||
|
||||
export const createSession = async (data: any): Promise<CheckoutSessionSetupResponse> => {
|
||||
return await httpPost('sessions', { ...data, lineItems: paymentsConfig.lineItems });
|
||||
};
|
||||
|
||||
export const checkBalance = async (
|
||||
giftcardStateData: any
|
||||
): Promise<{
|
||||
pspReference: string;
|
||||
resultCode: string;
|
||||
balance: {
|
||||
currency: string;
|
||||
value: number;
|
||||
};
|
||||
}> => await httpPost('paymentMethods/balance', giftcardStateData);
|
||||
|
||||
export const createOrder = async (amount: PaymentAmount): Promise<Order & OrderStatus> =>
|
||||
await httpPost('orders', { reference: `order-reference-${Date.now()}`, amount });
|
||||
|
||||
export const cancelOrder = async (order: Order): Promise<{ resultCode: string; pspReference: string }> => await httpPost('orders/cancel', order);
|
||||
105
packages/lib/storybook/helpers/checkout-handlers.ts
Normal file
105
packages/lib/storybook/helpers/checkout-handlers.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { makePayment, makeDetailsCall, getPaymentMethods } from './checkout-api-calls';
|
||||
import UIElement from '../../src/components/UIElement';
|
||||
import Core from '../../src/core';
|
||||
|
||||
function displayResultMessage(isAuthorized: boolean, resultCode: string): void {
|
||||
const image = document.createElement('img');
|
||||
image.setAttribute(
|
||||
'src',
|
||||
isAuthorized
|
||||
? 'https://checkoutshopper-test.adyen.com/checkoutshopper/images/components/success.gif'
|
||||
: 'https://checkoutshopper-test.adyen.com/checkoutshopper/images/components/error.gif'
|
||||
);
|
||||
image.setAttribute('height', '100');
|
||||
image.style.display = 'flex';
|
||||
image.style.margin = 'auto auto 30px';
|
||||
|
||||
const resultText = document.createElement('div');
|
||||
resultText.setAttribute('data-testid', 'result-message');
|
||||
resultText.style.textAlign = 'center';
|
||||
resultText.textContent = resultCode;
|
||||
|
||||
const container = document.getElementById('component-root');
|
||||
container.appendChild(image);
|
||||
container.appendChild(resultText);
|
||||
}
|
||||
|
||||
export function handleFinalState(result: any, component: UIElement): void {
|
||||
const isDropin = component?.props?.isDropin;
|
||||
const isAuthorized = result.resultCode === 'Authorised' || result.resultCode === 'Received';
|
||||
|
||||
if (isDropin) {
|
||||
if (isAuthorized) {
|
||||
component.setStatus('success');
|
||||
} else {
|
||||
component.setStatus('error');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (component?.unmount) {
|
||||
component.unmount();
|
||||
}
|
||||
displayResultMessage(isAuthorized, result.resultCode);
|
||||
}
|
||||
|
||||
export async function handleResponse(response, component, checkout, paymentData?) {
|
||||
if (response.action) {
|
||||
component.handleAction(response.action);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.order && response.order?.remainingAmount?.value > 0) {
|
||||
const order = {
|
||||
orderData: response.order.orderData,
|
||||
pspReference: response.order.pspReference
|
||||
};
|
||||
|
||||
const orderPaymentMethods = await getPaymentMethods({
|
||||
order,
|
||||
amount: paymentData.amount,
|
||||
shopperLocale: paymentData.shopperLocale
|
||||
});
|
||||
|
||||
checkout.update({
|
||||
paymentMethodsResponse: orderPaymentMethods,
|
||||
order,
|
||||
amount: response.order.remainingAmount
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
handleFinalState(response, component);
|
||||
}
|
||||
|
||||
export function handleChange(state: any, component: UIElement) {
|
||||
console.groupCollapsed(`onChange - ${state.data.paymentMethod.type}`);
|
||||
console.log('isValid', state.isValid);
|
||||
console.log('data', state.data);
|
||||
console.log('node', component._node);
|
||||
console.log('state', state);
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
export function handleError(error, component) {
|
||||
// SecuredField related errors should not go straight to console.error
|
||||
if (error.type === 'card') {
|
||||
console.log('### Card::onError:: obj=', error);
|
||||
} else {
|
||||
console.error(error.name, error.message, error.stack, component);
|
||||
}
|
||||
}
|
||||
|
||||
export async function handleSubmit(state: any, component: UIElement, checkout: Core, paymentData: any) {
|
||||
component.setStatus('loading');
|
||||
const response = await makePayment(state.data, paymentData);
|
||||
component.setStatus('ready');
|
||||
await handleResponse(response, component, checkout, paymentData);
|
||||
}
|
||||
|
||||
export async function handleAdditionalDetails(details, component: UIElement, checkout: Core) {
|
||||
component.setStatus('loading');
|
||||
const response = await makeDetailsCall(details.data);
|
||||
component.setStatus('ready');
|
||||
await handleResponse(response, component, checkout);
|
||||
}
|
||||
85
packages/lib/storybook/helpers/create-advanced-checkout.ts
Normal file
85
packages/lib/storybook/helpers/create-advanced-checkout.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import AdyenCheckout from '../../src/index';
|
||||
import { cancelOrder, checkBalance, createOrder, getPaymentMethods } from './checkout-api-calls';
|
||||
import { handleAdditionalDetails, handleChange, handleError, handleSubmit } from './checkout-handlers';
|
||||
import getCurrency from '../utils/get-currency';
|
||||
import { AdyenCheckoutProps } from '../stories/types';
|
||||
import { PaymentMethodsResponse } from '../../src/core/ProcessResponse/PaymentMethodsResponse/types';
|
||||
import Checkout from '../../src/core/core';
|
||||
|
||||
async function createAdvancedFlowCheckout({
|
||||
showPayButton,
|
||||
paymentMethodsConfiguration,
|
||||
countryCode,
|
||||
shopperLocale,
|
||||
amount
|
||||
}: AdyenCheckoutProps): Promise<Checkout> {
|
||||
const paymentAmount = {
|
||||
currency: getCurrency(countryCode),
|
||||
value: Number(amount)
|
||||
};
|
||||
|
||||
const paymentMethodsResponse: PaymentMethodsResponse = await getPaymentMethods({ amount: paymentAmount, shopperLocale, countryCode });
|
||||
|
||||
const checkout = await AdyenCheckout({
|
||||
clientKey: process.env.CLIENT_KEY,
|
||||
environment: process.env.CLIENT_ENV,
|
||||
amount: paymentAmount,
|
||||
countryCode,
|
||||
paymentMethodsResponse,
|
||||
locale: shopperLocale,
|
||||
showPayButton,
|
||||
paymentMethodsConfiguration,
|
||||
|
||||
onSubmit: (state, component) => {
|
||||
const paymentData = {
|
||||
amount: paymentAmount,
|
||||
countryCode,
|
||||
shopperLocale
|
||||
};
|
||||
handleSubmit(state, component, checkout, paymentData);
|
||||
},
|
||||
|
||||
onChange: (state, component) => {
|
||||
handleChange(state, component);
|
||||
},
|
||||
|
||||
onAdditionalDetails: async (state, component) => {
|
||||
await handleAdditionalDetails(state, component, checkout);
|
||||
},
|
||||
|
||||
onBalanceCheck: async (resolve, reject, data) => {
|
||||
try {
|
||||
const res = await checkBalance(data);
|
||||
resolve(res);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
|
||||
onOrderRequest: async (resolve, reject) => {
|
||||
try {
|
||||
const order = await createOrder(paymentAmount);
|
||||
resolve(order);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
|
||||
onOrderCancel: async order => {
|
||||
await cancelOrder(order);
|
||||
await checkout.update({
|
||||
paymentMethodsResponse: await getPaymentMethods({ amount: paymentAmount, shopperLocale, countryCode }),
|
||||
order: null,
|
||||
amount: paymentAmount
|
||||
});
|
||||
},
|
||||
|
||||
onError: (error, component) => {
|
||||
handleError(error, component);
|
||||
}
|
||||
});
|
||||
|
||||
return checkout;
|
||||
}
|
||||
|
||||
export { createAdvancedFlowCheckout };
|
||||
18
packages/lib/storybook/helpers/create-checkout.ts
Normal file
18
packages/lib/storybook/helpers/create-checkout.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createSessionsCheckout } from './create-sessions-checkout';
|
||||
import { createAdvancedFlowCheckout } from './create-advanced-checkout';
|
||||
|
||||
async function createCheckout(context: any): Promise<any> {
|
||||
const { useSessions, paymentMethodsConfiguration, showPayButton, countryCode, shopperLocale, amount } = context.args;
|
||||
|
||||
return useSessions
|
||||
? await createSessionsCheckout({ showPayButton, paymentMethodsConfiguration, countryCode, shopperLocale, amount })
|
||||
: await createAdvancedFlowCheckout({
|
||||
paymentMethodsConfiguration,
|
||||
showPayButton,
|
||||
countryCode,
|
||||
shopperLocale,
|
||||
amount
|
||||
});
|
||||
}
|
||||
|
||||
export { createCheckout };
|
||||
56
packages/lib/storybook/helpers/create-sessions-checkout.ts
Normal file
56
packages/lib/storybook/helpers/create-sessions-checkout.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import AdyenCheckout from '../../src/index';
|
||||
import { createSession } from './checkout-api-calls';
|
||||
import { RETURN_URL, SHOPPER_REFERENCE } from '../config/commonConfig';
|
||||
import { handleChange, handleError, handleFinalState } from './checkout-handlers';
|
||||
import getCurrency from '../utils/get-currency';
|
||||
import { AdyenCheckoutProps } from '../stories/types';
|
||||
import Checkout from '../../src/core/core';
|
||||
|
||||
async function createSessionsCheckout({
|
||||
showPayButton,
|
||||
paymentMethodsConfiguration,
|
||||
countryCode,
|
||||
shopperLocale,
|
||||
amount
|
||||
}: AdyenCheckoutProps): Promise<Checkout> {
|
||||
const session = await createSession({
|
||||
amount: {
|
||||
currency: getCurrency(countryCode),
|
||||
value: Number(amount)
|
||||
},
|
||||
shopperLocale,
|
||||
countryCode,
|
||||
reference: 'ABC123',
|
||||
returnUrl: RETURN_URL,
|
||||
shopperReference: SHOPPER_REFERENCE,
|
||||
shopperEmail: 'shopper.ctp1@adyen.com'
|
||||
});
|
||||
|
||||
const checkout = await AdyenCheckout({
|
||||
clientKey: process.env.CLIENT_KEY,
|
||||
environment: process.env.CLIENT_ENV,
|
||||
session,
|
||||
showPayButton,
|
||||
paymentMethodsConfiguration,
|
||||
// @ts-ignore TODO: Fix beforeSubmit type
|
||||
beforeSubmit: (data, component, actions) => {
|
||||
actions.resolve(data);
|
||||
},
|
||||
|
||||
onPaymentCompleted: (result, component) => {
|
||||
handleFinalState(result, component);
|
||||
},
|
||||
|
||||
onError: (error, component) => {
|
||||
handleError(error, component);
|
||||
},
|
||||
|
||||
onChange: (state, component) => {
|
||||
handleChange(state, component);
|
||||
}
|
||||
});
|
||||
|
||||
return checkout;
|
||||
}
|
||||
|
||||
export { createSessionsCheckout };
|
||||
22
packages/lib/storybook/stories/Container.tsx
Normal file
22
packages/lib/storybook/stories/Container.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
import Core from '../../src/core';
|
||||
import { PaymentMethodOptions, PaymentMethods } from '../../src/types';
|
||||
|
||||
interface IContainer<T extends keyof PaymentMethods> {
|
||||
type: T;
|
||||
componentConfiguration: PaymentMethodOptions<T>;
|
||||
checkout: Core;
|
||||
}
|
||||
|
||||
export const Container = <T extends keyof PaymentMethods>({ type, componentConfiguration, checkout }: IContainer<T>) => {
|
||||
const container = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!checkout) {
|
||||
return;
|
||||
}
|
||||
checkout.create(type, { ...componentConfiguration }).mount(container.current);
|
||||
}, []);
|
||||
|
||||
return <div ref={container} id="component-root" className="component-wrapper" />;
|
||||
};
|
||||
37
packages/lib/storybook/stories/Dropin.stories.tsx
Normal file
37
packages/lib/storybook/stories/Dropin.stories.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { DropinStoryProps } from './types';
|
||||
import { getStoryContextCheckout } from '../utils/get-story-context-checkout';
|
||||
import { Container } from './Container';
|
||||
|
||||
type DropinStory = StoryObj<DropinStoryProps>;
|
||||
|
||||
const meta: Meta<DropinStoryProps> = {
|
||||
title: 'Dropin/Default',
|
||||
argTypes: {
|
||||
componentConfiguration: {
|
||||
control: 'object'
|
||||
},
|
||||
paymentMethodsConfiguration: {
|
||||
control: 'object'
|
||||
}
|
||||
},
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
instantPaymentTypes: ['googlepay']
|
||||
},
|
||||
paymentMethodsConfiguration: {
|
||||
googlepay: {
|
||||
buttonType: 'plain'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default: DropinStory = {
|
||||
render: (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'dropin'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
}
|
||||
};
|
||||
116
packages/lib/storybook/stories/cards/Card.stories.tsx
Normal file
116
packages/lib/storybook/stories/cards/Card.stories.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { CardElementProps } from '../../../src/components/Card/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type CardStory = StoryObj<PaymentMethodStoryProps<CardElementProps> & { txVariant: string }>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<CardElementProps>> = {
|
||||
title: 'Cards/Card'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
const createComponent = (args, context) => {
|
||||
const { txVariant = 'card', componentConfiguration } = args;
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={txVariant} componentConfiguration={componentConfiguration} checkout={checkout} />;
|
||||
};
|
||||
|
||||
export const Default: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
_disableClickToPay: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const WithAVS: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
_disableClickToPay: true,
|
||||
billingAddressRequired: true,
|
||||
billingAddressAllowedCountries: ['US', 'CA', 'GB'],
|
||||
data: {
|
||||
billingAddress: {
|
||||
street: 'Virginia Street',
|
||||
postalCode: '95014',
|
||||
city: 'Cupertino',
|
||||
houseNumberOrName: '1',
|
||||
country: 'US',
|
||||
stateOrProvince: 'CA'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const WithPartialAVS: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
_disableClickToPay: true,
|
||||
billingAddressRequired: true,
|
||||
billingAddressMode: 'partial'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const WithInstallments: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
_disableClickToPay: true,
|
||||
showBrandsUnderCardNumber: true,
|
||||
showInstallmentAmounts: true,
|
||||
installmentOptions: {
|
||||
mc: {
|
||||
values: [1, 2, 3]
|
||||
},
|
||||
visa: {
|
||||
values: [1, 2, 3, 4],
|
||||
plans: ['regular', 'revolving']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const BCMC: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
txVariant: 'bcmc',
|
||||
componentConfiguration: {
|
||||
_disableClickToPay: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const KCP: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
_disableClickToPay: true,
|
||||
// Set koreanAuthenticationRequired AND countryCode so KCP fields show at start
|
||||
// Just set koreanAuthenticationRequired if KCP fields should only show if korean_local_card entered
|
||||
configuration: {
|
||||
koreanAuthenticationRequired: true
|
||||
},
|
||||
countryCode: 'KR'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const WithClickToPay: CardStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
componentConfiguration: {
|
||||
clickToPayConfiguration: {
|
||||
shopperEmail: 'gui.ctp@adyen.com',
|
||||
merchantDisplayName: 'Adyen Merchant Name'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
35
packages/lib/storybook/stories/components/Pix.stories.tsx
Normal file
35
packages/lib/storybook/stories/components/Pix.stories.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { PixProps } from '../../../src/components/Pix/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type PixStory = StoryObj<PaymentMethodStoryProps<PixProps>>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<PixProps>> = {
|
||||
title: 'Components/Pix'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
const createComponent = (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'pix'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
};
|
||||
|
||||
export const Default: PixStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
countryCode: 'BR'
|
||||
}
|
||||
};
|
||||
|
||||
export const WithPersonalDetails: PixStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
...Default.args,
|
||||
// @ts-ignore TODO: Make Pix 'introduction' prop optional
|
||||
componentConfiguration: {
|
||||
personalDetailsRequired: true
|
||||
}
|
||||
}
|
||||
};
|
||||
26
packages/lib/storybook/stories/components/UPI.stories.tsx
Normal file
26
packages/lib/storybook/stories/components/UPI.stories.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { UPIElementProps } from '../../../src/components/UPI/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type UpiStory = StoryObj<PaymentMethodStoryProps<UPIElementProps>>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<UPIElementProps>> = {
|
||||
title: 'Components/UPI'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const UPI: UpiStory = {
|
||||
render: (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'upi'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
},
|
||||
args: {
|
||||
countryCode: 'IN',
|
||||
componentConfiguration: {
|
||||
// @ts-ignore Seems like enum isnt the best way to export fixed strings
|
||||
defaultMode: 'vpa'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { getSearchParameter } from '../../utils/get-query-parameters';
|
||||
import { RedirectResultContainer } from './RedirectResultContainer';
|
||||
|
||||
type RedirectResultProps = {
|
||||
redirectResult: string;
|
||||
sessionId: string;
|
||||
};
|
||||
|
||||
type RedirectStory = StoryObj<RedirectResultProps>;
|
||||
|
||||
const meta: Meta<RedirectResultProps> = {
|
||||
title: 'Helpers/RedirectResult'
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const RedirectResult: RedirectStory = {
|
||||
render: args => <RedirectResultContainer {...args} />,
|
||||
args: {
|
||||
redirectResult: getSearchParameter('redirectResult'),
|
||||
sessionId: getSearchParameter('sessionId')
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import AdyenCheckout from '../../../src';
|
||||
import { handleError, handleFinalState } from '../../helpers/checkout-handlers';
|
||||
|
||||
export const RedirectResultContainer = ({ redirectResult, sessionId }) => {
|
||||
const [isRedirecting, setIsRedirecting] = useState<boolean>(true);
|
||||
let message = isRedirecting ? 'Submitting details...' : '';
|
||||
|
||||
useEffect(() => {
|
||||
if (!redirectResult || !sessionId) {
|
||||
message = 'There is no redirectResult / sessionId provided';
|
||||
return;
|
||||
}
|
||||
|
||||
AdyenCheckout({
|
||||
clientKey: process.env.CLIENT_KEY,
|
||||
environment: process.env.CLIENT_ENV,
|
||||
session: { id: sessionId },
|
||||
onPaymentCompleted: (result, component) => {
|
||||
setIsRedirecting(false);
|
||||
handleFinalState(result, component);
|
||||
},
|
||||
onError: (error, component) => {
|
||||
setIsRedirecting(false);
|
||||
handleError(error, component);
|
||||
}
|
||||
}).then(checkout => {
|
||||
setIsRedirecting(true);
|
||||
checkout.submitDetails({ details: { redirectResult } });
|
||||
});
|
||||
}, [sessionId]);
|
||||
|
||||
return (
|
||||
<div id="component-root" className="component-wrapper">
|
||||
{message}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { UIElementProps } from '../../../src/components/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type DotpayStory = StoryObj<PaymentMethodStoryProps<UIElementProps>>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<UIElementProps>> = {
|
||||
title: 'IssuerList/Dotpay'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const Dotpay: DotpayStory = {
|
||||
render: (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'dotpay'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
},
|
||||
args: {
|
||||
countryCode: 'PL'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { UIElementProps } from '../../../src/components/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type EntercashStory = StoryObj<PaymentMethodStoryProps<UIElementProps>>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<UIElementProps>> = {
|
||||
title: 'IssuerList/Entercash'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const Entercash: EntercashStory = {
|
||||
render: (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'entercash'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
},
|
||||
args: {
|
||||
countryCode: 'FI'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { Container } from '../Container';
|
||||
import { UIElementProps } from '../../../src/components/types';
|
||||
|
||||
type IdealStory = StoryObj<PaymentMethodStoryProps<UIElementProps>>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<UIElementProps>> = {
|
||||
title: 'IssuerList/IDEAL'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
const createComponent = (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'ideal'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
};
|
||||
|
||||
export const Default: IdealStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
countryCode: 'NL'
|
||||
}
|
||||
};
|
||||
|
||||
export const WithHighlightedIssuers: IdealStory = {
|
||||
render: createComponent,
|
||||
args: {
|
||||
...Default.args,
|
||||
componentConfiguration: {
|
||||
// @ts-ignore TODO: 'highlightedIssuers' is not documented
|
||||
highlightedIssuers: ['1121', '1154', '1153']
|
||||
}
|
||||
}
|
||||
};
|
||||
25
packages/lib/storybook/stories/types.ts
Normal file
25
packages/lib/storybook/stories/types.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { DropinElementProps } from '../../src/components/Dropin/types';
|
||||
|
||||
type GlobalStoryProps = {
|
||||
useSessions: boolean;
|
||||
countryCode: string;
|
||||
shopperLocale: string;
|
||||
amount: number;
|
||||
showPayButton: boolean;
|
||||
};
|
||||
|
||||
export interface PaymentMethodStoryProps<T> extends GlobalStoryProps {
|
||||
componentConfiguration: T;
|
||||
}
|
||||
|
||||
export interface DropinStoryProps extends PaymentMethodStoryProps<DropinElementProps> {
|
||||
paymentMethodsConfiguration: any;
|
||||
}
|
||||
|
||||
export type AdyenCheckoutProps = {
|
||||
showPayButton: boolean;
|
||||
paymentMethodsConfiguration?: Record<string, object>;
|
||||
countryCode: string;
|
||||
shopperLocale: string;
|
||||
amount: number;
|
||||
};
|
||||
22
packages/lib/storybook/stories/vouchers/Oxxo.stories.tsx
Normal file
22
packages/lib/storybook/stories/vouchers/Oxxo.stories.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { UIElementProps } from '../../../src/components/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type OxxoStory = StoryObj<PaymentMethodStoryProps<UIElementProps>>;
|
||||
|
||||
const meta: Meta<PaymentMethodStoryProps<UIElementProps>> = {
|
||||
title: 'Vouchers/Oxxo'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const Oxxo: OxxoStory = {
|
||||
render: (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'oxxo'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
},
|
||||
args: {
|
||||
countryCode: 'MX'
|
||||
}
|
||||
};
|
||||
19
packages/lib/storybook/stories/wallets/PayPal.stories.tsx
Normal file
19
packages/lib/storybook/stories/wallets/PayPal.stories.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Meta, StoryObj } from '@storybook/preact';
|
||||
import { PaymentMethodStoryProps } from '../types';
|
||||
import { getStoryContextCheckout } from '../../utils/get-story-context-checkout';
|
||||
import { PayPalElementProps } from '../../../src/components/PayPal/types';
|
||||
import { Container } from '../Container';
|
||||
|
||||
type Story = StoryObj<PaymentMethodStoryProps<PayPalElementProps>>;
|
||||
|
||||
const meta: Meta = {
|
||||
title: 'Wallets/Paypal'
|
||||
};
|
||||
export default meta;
|
||||
|
||||
export const Paypal: Story = {
|
||||
render: (args, context) => {
|
||||
const checkout = getStoryContextCheckout(context);
|
||||
return <Container type={'paypal'} componentConfiguration={args.componentConfiguration} checkout={checkout} />;
|
||||
}
|
||||
};
|
||||
8
packages/lib/storybook/utils/add-to-window.ts
Normal file
8
packages/lib/storybook/utils/add-to-window.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { IUIElement } from '../../src/components/types';
|
||||
|
||||
const addToWindow = (component: IUIElement) => {
|
||||
// @ts-ignore ignore
|
||||
window.component = component;
|
||||
};
|
||||
|
||||
export { addToWindow };
|
||||
39
packages/lib/storybook/utils/get-currency.ts
Normal file
39
packages/lib/storybook/utils/get-currency.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
const currencies: Record<string, string> = {
|
||||
AR: 'ARS',
|
||||
AU: 'AUD',
|
||||
BR: 'BRL',
|
||||
CA: 'CAD',
|
||||
CH: 'CHF',
|
||||
CN: 'CNY',
|
||||
CZ: 'CZK',
|
||||
DK: 'DKK',
|
||||
GB: 'GBP',
|
||||
HK: 'HKD',
|
||||
HR: 'HRK',
|
||||
HU: 'HUN',
|
||||
ID: 'IDR',
|
||||
IN: 'INR',
|
||||
JP: 'JPY',
|
||||
KR: 'KRW',
|
||||
MG: 'MGA',
|
||||
MX: 'MXN',
|
||||
MY: 'MYR',
|
||||
NO: 'NOK',
|
||||
NZ: 'NZD',
|
||||
PH: 'PHP',
|
||||
PL: 'PLN',
|
||||
RO: 'RON',
|
||||
RU: 'RUB',
|
||||
SE: 'SEK',
|
||||
SG: 'SGD',
|
||||
SK: 'SKK',
|
||||
TH: 'THB',
|
||||
TW: 'TWD',
|
||||
US: 'USD',
|
||||
VN: 'VND',
|
||||
default: 'EUR'
|
||||
};
|
||||
|
||||
const getCurrency = (countryCode: string): string => currencies[countryCode] || currencies.default;
|
||||
|
||||
export default getCurrency;
|
||||
14
packages/lib/storybook/utils/get-query-parameters.ts
Normal file
14
packages/lib/storybook/utils/get-query-parameters.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const getSearchParameter = (parameter: string, queryString = window.location.search): string => {
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
return urlParams.get(parameter);
|
||||
};
|
||||
|
||||
export const getSearchParameters = (queryString = window.location.search): Record<string, string> => {
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const parameters: Record<string, string> = {};
|
||||
// @ts-ignore
|
||||
for (const entry of urlParams.entries()) {
|
||||
parameters[entry[0]] = entry[1];
|
||||
}
|
||||
return parameters;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
import Core from '../../src/core';
|
||||
|
||||
const getStoryContextCheckout = (context): Core | undefined => {
|
||||
const { checkout } = context.loaded;
|
||||
return checkout;
|
||||
};
|
||||
|
||||
export { getStoryContextCheckout };
|
||||
13
packages/lib/storybook/utils/http-post.ts
Normal file
13
packages/lib/storybook/utils/http-post.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const { host, protocol } = window.location;
|
||||
|
||||
export async function httpPost<T>(endpoint: string, data: any): Promise<T> {
|
||||
const response = await fetch(`${protocol}//${host}/${endpoint}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
return await response.json();
|
||||
}
|
||||
Reference in New Issue
Block a user