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/custom theme #134

Merged
merged 25 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
be63fa6
feat(theme): add property customTheme, Theme interface
absolemDev Nov 15, 2023
9374544
feat(form): add disabled property
absolemDev Nov 15, 2023
6921103
feat(input, button): customization components, add interfaces
absolemDev Nov 16, 2023
96cd845
fix(theme-provider): add conditional render
absolemDev Nov 16, 2023
e005061
feat: test
Nelfimov Nov 17, 2023
2ec40ad
chore: deps
Nelfimov Nov 17, 2023
5398e3a
feat: add customElement property, interfaces, conditional render cust…
absolemDev Nov 20, 2023
9f9f1d8
fix: install dependencies
absolemDev Nov 20, 2023
c3e7e80
fix: imports, interfaces
absolemDev Nov 20, 2023
c9b1626
feat: add wrappers for custom elemrnts
absolemDev Nov 22, 2023
bf5c3ac
fix: interfaces
absolemDev Nov 22, 2023
2b0cd55
feat: add form provider, form context, hook useForm
absolemDev Nov 23, 2023
611572e
fix: interfaces
absolemDev Nov 23, 2023
2762700
fix(input-wrapper): transform children arguments to object
absolemDev Nov 23, 2023
e122833
feat: add disabled prop, arguments for cildren button wrapper, edit i…
absolemDev Nov 24, 2023
9ac4775
fix(form-provider): export const
absolemDev Nov 24, 2023
84addff
feat(enums): add ButtonType enum
absolemDev Nov 24, 2023
f8d6d3c
feat: add use-custom-element hook, extend form context, edit interfaces
absolemDev Nov 27, 2023
bf5b803
fix: useMemo props for custom-elements hook, use Condition in wrapper…
absolemDev Nov 28, 2023
78bbb3d
fix: add fields hook, button hook, utils
absolemDev Nov 28, 2023
4272da8
docs: add README.md
absolemDev Nov 28, 2023
cfc8b6b
docs: edit README.md
absolemDev Nov 29, 2023
759c5c0
fix: get-name-fields util
absolemDev Nov 29, 2023
6b3cc3d
docs: edit package name
absolemDev Nov 29, 2023
2da55e8
fix: yarn check
absolemDev Nov 29, 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
91 changes: 89 additions & 2 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .run/test-unit.run.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="test-unit" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="yarn test unit" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
4 changes: 4 additions & 0 deletions packages/payment-widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@
"styled-tools": "1.7.2"
},
"devDependencies": {
"@emotion/jest": "11.9.4",
"@emotion/react": "11.9.3",
"@emotion/styled": "11.9.3",
"@testing-library/react": "14.1.0",
"@types/react": "18.2.20",
"@types/react-dom": "18",
"@types/styled-system": "5.1.16",
"csstype": "3.1.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"styled-system": "5.1.5"
},
"peerDependencies": {
Expand Down
4 changes: 4 additions & 0 deletions packages/payment-widget/src/enums/button.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* eslint-disable no-shadow */
export enum ButtonType {
Submit = 'submit',
}
1 change: 1 addition & 0 deletions packages/payment-widget/src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './payment-method.enum'
export * from './payment-object.enum'
export * from './tax.enum'
export * from './taxation.enum'
export * from './button.enum'
1 change: 1 addition & 0 deletions packages/payment-widget/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './use-init.hook'
export { useValidate } from './use-validate.hook'
export { useFieldsState } from './use-fields-state.hook'
export { useFieldsRenderer } from './use-fields-render.hook'
export { useCustomElements } from './use-custom-elements.hook'
77 changes: 77 additions & 0 deletions packages/payment-widget/src/hooks/use-custom-elements.hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Children } from 'react'
import { ReactNode } from 'react'
import { JSXElementConstructor } from 'react'
import { isValidElement } from 'react'

import { AdditionalFieldsType } from '../enums'
import { RequiredFieldsType } from '../enums'
import { CustomElements } from '../interfaces'
import { Field } from '../interfaces'

export const useCustomElements = (
Nelfimov marked this conversation as resolved.
Show resolved Hide resolved
existAmount: boolean,
existReceipt: boolean,
existAdditionalFields: boolean,
nodes: ReactNode
): CustomElements => {
const nodeArray = Children.toArray(nodes)

const isCastomElement = (nameNode: string, node: ReactNode): boolean =>
isValidElement(node)
? (node.type as JSXElementConstructor<any>).name === nameNode &&
typeof node.props.children === 'function'
: false
const isAdditionalField = (node: ReactNode): boolean =>
isValidElement(node) ? Object.values(AdditionalFieldsType).includes(node.props.name) : false
const isRequiredField = (node: ReactNode): boolean =>
isValidElement(node) ? node.props.name === RequiredFieldsType.Amount && !existAmount : false

const customFields = nodeArray.filter(
(node) =>
isCastomElement('InputWrapper', node) && (isAdditionalField(node) || isRequiredField(node))
Nelfimov marked this conversation as resolved.
Show resolved Hide resolved
)
const nameFields = customFields.reduce<Field[]>((acc, field) => {
if (isValidElement(field))
acc.push({
name: AdditionalFieldsType[field.props.name] || RequiredFieldsType[field.props.name],
})
return acc
}, [])

const customButton = nodeArray.find((node) => isCastomElement('ButtonWrapper', node))

const isGenerateReceiptField = existReceipt && !customFields.length

const isGenerateRequiredField = !existAmount && existAdditionalFields

const existCustomAmountField = customFields.some(
(field) => isValidElement(field) && field.props.name === RequiredFieldsType.Amount
)
const existCustomReceiptField = customFields.some(
(field) =>
isValidElement(field) &&
(field.props.name === AdditionalFieldsType.Email ||
field.props.name === AdditionalFieldsType.Phone)
)

if (existAdditionalFields && customFields.length)
throw new Error('Don`t use additionalFields property with InputWrapper component')

if (customFields.length && !existAmount && !existCustomAmountField)
throw new Error(
'If you use InputWrapper component and don`t set amount property, you mast use InputWrapper componnet with property name equal RequiredFieldsType.Amount'
)

if (customFields.length && existReceipt && !existCustomReceiptField)
throw new Error(
'If you set receipt property whith InputWrapper component, you mast use InputWrapper componnet with property name equal AdditionalFieldsType.Phone or AdditionalFieldsType.Email'
)

return {
customFields,
customButton,
isGenerateReceiptField,
isGenerateRequiredField,
nameFields,
}
}
52 changes: 29 additions & 23 deletions packages/payment-widget/src/hooks/use-fields-render.hook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ import React from 'react'
import { useIntl } from 'react-intl'

import { Field } from '../interfaces'
import { NameField } from '../interfaces'
import { HandleBlurField } from '../interfaces'
import { FieldState } from '../interfaces'
import { HandleChangeField } from '../interfaces'
import { FieldsErrors } from '../interfaces'
import { MemoizedInput } from '../ui'
import { ThemeProvider } from '../ui/theme/src'
import { translate } from '../utils/translate.util'

export const useFieldsRenderer = (
fields: Field[],
fields: Field[] | NameField[],
errors: FieldsErrors,
fieldsState: FieldState,
handleChange: HandleChangeField,
Expand All @@ -22,27 +24,31 @@ export const useFieldsRenderer = (
) => {
const intl = useIntl()

return fields.map((field, index, currentFields) => {
const translatePlaceholder = translate(intl, field.placeholder, field.placeholder)
const translateError = translate(intl, errors[field.name], errors[field.name])
const isNotLastField = index !== currentFields.length - 1
return (
<ThemeProvider>
{fields.map((field, index, currentFields) => {
const translatePlaceholder = translate(intl, field.placeholder, field.placeholder)
const translateError = translate(intl, errors[field.name], errors[field.name])
const isNotLastField = index !== currentFields.length - 1

return (
<React.Fragment key={field.name}>
<MemoizedInput
type={field.type ?? 'text'}
name={field.name}
placeholder={translatePlaceholder}
required={field.required ?? false}
value={fieldsState[field.name]}
errorText={translateError}
onChangeNative={handleChange}
onBlur={handleBlur}
/>
<Condition match={isNotLastField}>
<Layout flexBasis={inputGaps} flexShrink={0} />
</Condition>
</React.Fragment>
)
})
return (
<React.Fragment key={field.name}>
<MemoizedInput
type={field.type ?? 'text'}
name={field.name}
placeholder={translatePlaceholder}
required={field.required ?? false}
value={fieldsState[field.name]}
errorText={translateError}
onChangeNative={handleChange}
onBlur={handleBlur}
/>
<Condition match={isNotLastField}>
<Layout flexBasis={inputGaps} flexShrink={0} />
</Condition>
</React.Fragment>
)
})}
</ThemeProvider>
)
}
3 changes: 2 additions & 1 deletion packages/payment-widget/src/hooks/use-fields-state.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { useCallback } from 'react'
import { useState } from 'react'

import { Field } from '../interfaces'
import { FieldsState } from '../interfaces'
import { HandleBlurField } from '../interfaces'
import { FieldState } from '../interfaces'
import { HandleChangeField } from '../interfaces'
import { FieldsNames } from '../interfaces'
import { ValidateField } from '../interfaces'

export const useFieldsState = (fields: Field[], validateField: ValidateField) => {
export const useFieldsState = (validateField: ValidateField, fields: Field[]): FieldsState => {
const initialState = fields.reduce((acc, field) => ({ ...acc, [field.name]: '' }), {})
const [fieldsState, setFieldsState] = useState<FieldState>(initialState as FieldState)

Expand Down
2 changes: 1 addition & 1 deletion packages/payment-widget/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { Widget } from './ui'
export { Widget, InputWrapper, ButtonWrapper } from './ui'
export {
Settings,
Styles,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ReactNode } from 'react'

import { Field } from './fields.interfaces'

export type NameField = Pick<Field, 'name'>

export interface CustomElements {
TorinAsakura marked this conversation as resolved.
Show resolved Hide resolved
customFields: ReactNode[]
customButton: ReactNode
isGenerateReceiptField: boolean
isGenerateRequiredField: boolean
nameFields: Field[]
}
Loading
Loading