Skip to content

Commit

Permalink
feat: wip adding system to generate buttons and blocks from same prop…
Browse files Browse the repository at this point in the history
…, common types, etc
  • Loading branch information
Quentin Le Caignec committed Aug 27, 2024
1 parent ac486c8 commit 10088ae
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,47 @@
import { Alien, Leaf, TreasureChest } from '@phosphor-icons/react';
import type {
IFormDynBlock,
IFormField,
IFormRegisterFunc,
} from '../../types/form-dynamic-zone';
import type { ReactElement } from 'react';

import { Alien, Cube, Leaf, TreasureChest } from '@phosphor-icons/react';

export const blockOptionsMock = [
{ id: 'default', label: 'Default', leftSection: <Alien /> },
{ id: 'other', label: 'Other', leftSection: <Leaf /> },
{ id: 'stuff', label: 'Stuff', leftSection: <TreasureChest /> },
];

export const dynamicBlocksMock: IFormDynBlock[] = [
{
block: {
blockHeader: (
<>
<Cube key="1" />
<span key="2">Example</span>
</>
),
blockType: 'default',
value: 'initial',
},
button: {
blockType: 'default',
label: 'Example',
leftSection: <Cube />,
},
renderFunc: (
b: IFormField,
_i: number,
register: IFormRegisterFunc,
registerName: string,
): ReactElement => {
return (
<>
<span>works</span>
<input key={b.id} {...register(registerName)} />
</>
);
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { ReactElement } from 'react';
import { FormProvider, useForm } from 'react-hook-form';

import { FormDynamicZone as Cmp } from './FormDynamicZone';
import { dynamicBlocksMock } from './FormDynamicZone.mock';

const meta = {
component: Cmp,
Expand All @@ -27,6 +28,6 @@ function render() {
}

export const FormDynamicZone: IStory = {
args: { dynamicZoneName: 'dynTest' },
args: { dynamicBlocks: dynamicBlocksMock, dynamicZoneName: 'dynTest' },
render: render(),
};
Original file line number Diff line number Diff line change
@@ -1,52 +1,62 @@
import type { IBaseBlock } from '@smile/haring-react';
import type { IFormDynBlock, IFormField } from '../../types/form-dynamic-zone';
import type { IBaseBlockButton, IBaseBlockType } from '@smile/haring-react';
import type { ReactElement } from 'react';

import { Flex } from '@mantine/core';
import { DynamicZone } from '@smile/haring-react';
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';

import { blockOptionsMock } from './FormDynamicZone.mock';

interface IFormBlock extends IBaseBlock {
fieldName: string;
value?: string;
}

export interface IFormDynamicZoneProps {
dynamicBlocks: IFormDynBlock[];
dynamicZoneName: string;
}

export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
const { dynamicZoneName } = props;
const { dynamicBlocks, dynamicZoneName } = props;

const { control, register, handleSubmit } =
useFormContext<Record<typeof dynamicZoneName, Omit<IFormBlock, 'id'>[]>>();
useFormContext<Record<typeof dynamicZoneName, Omit<IFormField, 'id'>[]>>();
const { fields, append, remove, swap, update } = useFieldArray({
control,
name: dynamicZoneName,
});
const watched = useWatch({ control, name: dynamicZoneName });

function newBlock(block: IFormBlock, index: number): ReactElement {
return (
<input
key={block.id}
{...register(`${dynamicZoneName}.${index}.value` as const)}
/>
const blockOptions: IBaseBlockButton[] = dynamicBlocks.map((b) => b.button);

function onAppend(type: IBaseBlockType): void {
const correspondingType = dynamicBlocks.find(
(b) => b.block.blockType === type,
);
if (correspondingType === undefined) {
throw Error(
`Could not find an IFormDynBlock of blocktype '${type} in given dynamicBlocks'`,
);
}
append({
...correspondingType.block,
});
}

function onToggle(block: IFormBlock, index: number, opened: boolean): void {
update(index, { ...block, opened });
function renderBlock(block: IFormField, index: number): ReactElement {
const correspondingType = dynamicBlocks.find(
(b) => b.block.blockType === block.blockType,
);
if (correspondingType === undefined) {
throw Error(
`Could not find an IFormDynBlock of blocktype '${block.blockType} in given dynamicBlocks'`,
);
}
return correspondingType.renderFunc(
block,
index,
register,
`${dynamicZoneName}.${index}.value` as const,
);
}

function onAppend(blockType: string): void {
append({
fieldName: `${blockType}-${fields.length}`,
opened: false,
type: blockType,
value: '',
});
function onToggle(block: IFormField, index: number, opened: boolean): void {
update(index, { ...block, opened });
}

function onSubmit(data: unknown): void {
Expand All @@ -68,12 +78,12 @@ export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
</Flex>
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
<form onSubmit={handleSubmit(onSubmit)}>
form container, {dynamicZoneName}: {JSON.stringify(watched)}
<DynamicZone<IFormBlock>
blockOptions={blockOptionsMock}
blocks={fields as IFormBlock[]}
form container, {dynamicZoneName}: {JSON.stringify(watched, null, 2)}
<DynamicZone<IFormField>
blockOptions={blockOptions}
blocks={fields as IFormField[]}
onAppendBlock={onAppend}
onRenderBlockContent={newBlock}
onRenderBlockContent={renderBlock}
onToggleBlock={onToggle}
/>
<input type="submit" />
Expand Down
22 changes: 22 additions & 0 deletions packages/haring-react-hook-form/src/types/form-dynamic-zone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { IBaseBlock, IBaseBlockButton } from '@smile/haring-react';
import type { ReactElement } from 'react';
import type { UseFormRegister } from 'react-hook-form/dist/types/form';

export interface IFormField extends IBaseBlock {
// fieldName: string;
value?: string;
}

export type IFormRegisterFunc<T extends IFormField = IFormField> =
UseFormRegister<Record<string, Omit<T, 'id'>[]>>;

export interface IFormDynBlock {
block: Omit<IFormField, 'id'>;
button: IBaseBlockButton;
renderFunc: (
block: IBaseBlock,
index: number,
registerFunc: IFormRegisterFunc,
registerName: string,
) => ReactElement;
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const dynamicZoneBlocks: IBaseBlock[] = [
First
</>
),
blockType: 'default',
id: '1',
opened: false,
value: 'initial',
Expand All @@ -59,6 +60,7 @@ export const dynamicZoneBlocks: IBaseBlock[] = [
Second
</>
),
blockType: 'default',
id: '2',
opened: true,
value: 'initial',
Expand All @@ -72,14 +74,15 @@ export const dynamicZoneBlocks: IBaseBlock[] = [
Third
</>
),
blockType: 'default',
id: '3',
opened: false,
value: 'initial',
},
];

export const dynamicZoneButtons: IBaseBlockButton[] = [
{ id: 'default', label: 'Default', leftSection: <Alien /> },
{ id: 'other', label: 'Other', leftSection: <Leaf /> },
{ id: 'stuff', label: 'Stuff', leftSection: <TreasureChest /> },
{ blockType: 'default', label: 'Default', leftSection: <Alien /> },
{ blockType: 'other', label: 'Other', leftSection: <Leaf /> },
{ blockType: 'stuff', label: 'Stuff', leftSection: <TreasureChest /> },
];
12 changes: 6 additions & 6 deletions packages/haring-react/src/Form/DynamicZone/DynamicZone.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IDynamicZoneBlockInternalComponentProps } from './DynamicZoneBlock/DynamicZoneBlock';
import type { IBaseBlock, IBaseBlockButton } from '../../types';
import type { IBaseBlock, IBaseBlockButton, IBaseBlockType } from '../../types';
import type {
CardProps,
ContainerProps,
Expand All @@ -24,7 +24,7 @@ export interface IDynamicZoneProps<Block extends IBaseBlock> {
buttonsText?: string;
buttonsTextProps?: TextProps;
internalBlockCardProps?: IDynamicZoneBlockInternalComponentProps;
onAppendBlock: (blockType: string) => void;
onAppendBlock: (blockType: IBaseBlockType) => void;
onRenderBlockContent: (block: Block, index: number) => ReactElement;
onToggleBlock: (block: Block, index: number, opened: boolean) => void;
rootContainerProps?: ContainerProps;
Expand All @@ -49,7 +49,7 @@ export function DynamicZone<Block extends IBaseBlock>(
rootContainerProps,
} = props;

function onAddBlock(blockType: string): void {
function onAddBlock(blockType: IBaseBlockType): void {
onAppendBlock(blockType);
}

Expand Down Expand Up @@ -83,15 +83,15 @@ export function DynamicZone<Block extends IBaseBlock>(
{buttonsText}
</Text>
<Group {...buttonsGroupProps}>
{blockOptions.map((button) => (
{blockOptions.map(({ blockType, ...button }) => (
<Button
radius="md"
size="md"
type="button"
variant="default"
{...button}
key={`button-${button.id}`}
onClick={() => onAddBlock(button.id)}
key={`button-${blockType}`}
onClick={() => onAddBlock(blockType)}
>
{button.label}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export function DynamicZoneBlock(props: IDynamicZoneBlockProps): ReactElement {
{opened ? toggleProps.downIcon : toggleProps.upIcon}
</ActionIcon>
<Space />
<>{headerChildren}</>
{headerChildren}
</Group>
{actions && actions.length > 0 ? (
<ActionList<IDynamicZoneBlockReference>
Expand Down
4 changes: 3 additions & 1 deletion packages/haring-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export type { IFetchAutocompleteFieldProps } from './Form/FetchAutocompleteField
export { FetchAutocompleteField } from './Form/FetchAutocompleteField/FetchAutocompleteField';
export type { IAddressAutocompleteFieldProps } from './Form/AddressGouvAutocompleteField/AddressGouvAutocompleteField';
export { AddressGouvAutocompleteField } from './Form/AddressGouvAutocompleteField/AddressGouvAutocompleteField';
// export type { IDynamicZoneProps } from './Form/DynamicZone/DynamicZone';
export type { IDynamicZoneProps } from './Form/DynamicZone/DynamicZone';
export { DynamicZone } from './Form/DynamicZone/DynamicZone';
export type { IAddressFieldsProps } from './Form/AddressFields/AddressFields';
export { AddressFields } from './Form/AddressFields/AddressFields';
Expand Down Expand Up @@ -131,4 +131,6 @@ export type {
IThumbnailAction,
IThumbnailData,
IBaseBlock,
IBaseBlockButton,
IBaseBlockType,
} from './types';
5 changes: 4 additions & 1 deletion packages/haring-react/src/types/dynamic-zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import type { ButtonProps } from '@mantine/core';
import type { IAction } from '@smile/haring-react-shared';
import type { ReactNode } from 'react';

export type IBaseBlockType = string;

export interface IBaseBlock extends Record<string, unknown> {
blockActions?: IAction<IDynamicZoneBlockReference>[];
blockFooter?: ReactNode;
blockHeader: ReactNode;
blockType: IBaseBlockType;
id: string;
opened: boolean;
}

export interface IBaseBlockButton extends ButtonProps {
id: string;
blockType: IBaseBlockType;
label: string;
}

0 comments on commit 10088ae

Please sign in to comment.