Skip to content

Commit

Permalink
feat: wip progress on FormDynamicZone
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin LE CAIGNEC committed Sep 20, 2024
1 parent b78828a commit 674fe70
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IDynamicZoneBlockReference } from './DynamicZoneBlock/DynamicZoneBlock';
import type { IBaseBlock, IBaseBlockButton } from '../../types';
import type { IBaseBlockButton, IBaseBlockFull } from '../../types';
import type { IAction } from '@smile/haring-react-shared';

import {
Expand Down Expand Up @@ -36,7 +36,7 @@ const dynamicZoneBlockActionsMock: IAction<IDynamicZoneBlockReference>[] = [
},
];

export const dynamicZoneBlocks: IBaseBlock[] = [
export const dynamicZoneBlocks: IBaseBlockFull[] = [
{
blockActions: dynamicZoneBlockActionsMock,
blockHeader: (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IBaseBlock } from '../../types';
import type { IBaseBlockOptions } from '../../types';
import type { ReactElement } from 'react';

import { renderWithProviders } from '@smile/haring-react-shared/test-utils';
Expand All @@ -10,7 +10,7 @@ import { dynamicZoneBlocks, dynamicZoneButtons } from './DynamicZone.mock';

describe('DynamicZone', () => {
it('matches snapshot', () => {
const onRender = (_b: IBaseBlock, index: number): ReactElement => (
const onRender = (_b: IBaseBlockOptions, index: number): ReactElement => (
<input key={index} />
);
const { container } = renderWithProviders(
Expand Down
23 changes: 14 additions & 9 deletions packages/haring-react/src/Form/DynamicZone/DynamicZone.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { IDynamicZoneBlockInternalComponentProps } from './DynamicZoneBlock/DynamicZoneBlock';
import type { IBaseBlock, IBaseBlockButton, IBaseBlockType } from '../../types';
import type {
IBaseBlockButton,
IBaseBlockFull,
IBaseBlockType,
} from '../../types';
import type {
CardProps,
ContainerProps,
Expand All @@ -14,25 +18,26 @@ import { Button, Container, Group, Stack, Text } from '@mantine/core';
import classes from './DynamicZone.module.css';
import { DynamicZoneBlock } from './DynamicZoneBlock/DynamicZoneBlock';

export interface IDynamicZoneProps<Block extends IBaseBlock>
extends ContainerProps {
export interface IDynamicZoneProps extends ContainerProps {
blockCardProps?: CardProps;
blockOptions: IBaseBlockButton[];
blocks: Block[];
blocks: IBaseBlockFull[];
blocksStackProps?: StackProps;
bottomContainerProps?: ContainerProps;
buttonsGroupProps?: GroupProps;
buttonsText?: string;
buttonsTextProps?: TextProps;
internalBlockCardProps?: IDynamicZoneBlockInternalComponentProps;
onAppendBlock: (blockType: IBaseBlockType) => void;
onRenderBlockContent: (block: Block, index: number) => ReactElement;
onToggleBlock: (block: Block, index: number, opened: boolean) => void;
onRenderBlockContent: (block: IBaseBlockFull, index: number) => ReactElement;
onToggleBlock: (
block: IBaseBlockFull,
index: number,
opened: boolean,
) => void;
}

export function DynamicZone<Block extends IBaseBlock>(
props: IDynamicZoneProps<Block>,
): ReactElement {
export function DynamicZone(props: IDynamicZoneProps): ReactElement {
const {
blockCardProps,
blockOptions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface IDynamicZoneBlockProps extends CardProps {
actions?: IAction<IDynamicZoneBlockReference>[];
children: ReactNode;
footerChildren?: ReactNode;
headerChildren: ReactNode;
headerChildren?: ReactNode;
internalComponentProps?: IDynamicZoneBlockInternalComponentProps;
onToggle: (opened: boolean) => void;
opened: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { IBaseBlock, IFormDynamicZoneBlock } from '../../types';
import type {
IBaseBlock,
IBaseBlockFull,
IFormDynamicZoneBlock,
} from '../../types';
import type { ReactElement } from 'react';

import { Group } from '@mantine/core';
Expand All @@ -8,7 +12,7 @@ export interface IExampleBlock extends IBaseBlock {
value?: string;
}

export const blocksMock: IExampleBlock[] = [
export const blocksMock: IBaseBlockFull<IExampleBlock>[] = [
{
blockHeader: (
<>
Expand Down Expand Up @@ -38,15 +42,17 @@ export const blocksMock: IExampleBlock[] = [
export const availableBlocksMock: IFormDynamicZoneBlock<IExampleBlock>[] = [
{
block: {
blockType: 'exampleA',
opened: true,
value: '',
},
blockOptions: {
blockHeader: (
<>
<Cube key="1" />
<span key="2">Example A</span>
</>
),
blockType: 'exampleA',
opened: true,
value: '',
},
button: {
blockType: 'exampleA',
Expand Down Expand Up @@ -75,15 +81,17 @@ export const availableBlocksMock: IFormDynamicZoneBlock<IExampleBlock>[] = [
},
{
block: {
blockType: 'exampleB',
opened: true,
value: '',
},
blockOptions: {
blockHeader: (
<>
<Leaf key="1" />
<span key="2">Example B</span>
</>
),
blockType: 'exampleB',
opened: true,
value: '',
},
button: {
blockType: 'exampleB',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ export const FormDynamicZone: IStory = {
args: {
availableBlocks: availableBlocksMock,
blocksArray: blocksMock,
onUpdatedArray: action('results'),
onAppendUpdate: action('append'),
onRemoveUpdate: action('remove'),
onSwapUpdate: action('swap'),
onToggleUpdate: action('toggle'),
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ describe('FormDynamicZone', () => {
<FormDynamicZone<IExampleBlock>
availableBlocks={availableBlocksMock}
blocksArray={blocksMock}
onUpdatedArray={action('onUpdatedArray')}
onAppendUpdate={action('append')}
onRemoveUpdate={action('remove')}
onSwapUpdate={action('swap')}
onToggleUpdate={action('toggle')}
/>,
);
expect(container).toMatchSnapshot();
Expand Down
77 changes: 42 additions & 35 deletions packages/haring-react/src/Form/FormDynamicZone/FormDynamicZone.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { IBaseBlock, IFormDynamicZoneBlock } from '../../types';
import type {
IBaseBlock,
IBaseBlockFull,
IFormDynamicZoneBlock,
} from '../../types';
import type { IBaseBlockButton, IBaseBlockType } from '@smile/haring-react';
import type { IDynamicZoneBlockReference } from '@smile/haring-react/src/Form/DynamicZone/DynamicZoneBlock/DynamicZoneBlock';
import type { IAction } from '@smile/haring-react-shared';
import type { ReactElement } from 'react';

import { ArrowDown, ArrowUp, Trash } from '@phosphor-icons/react';
import { isNotNullNorEmpty } from '@smile/haring-react-shared';

import { DynamicZone } from '../DynamicZone/DynamicZone';

Expand All @@ -20,26 +25,32 @@ const defaultActionLabels: IFormDynamicZoneActionLabels = {
moveUpLabel: 'Move Up',
};

export interface IFormDynamicZoneProps<T extends IBaseBlock> {
export interface IFormDynamicZoneProps<Block extends IBaseBlock> {
actionLabels?: IFormDynamicZoneActionLabels;
availableBlocks: IFormDynamicZoneBlock<T>[];
blocksArray: T[];
onUpdatedArray: (newBlocksArray: T[]) => void;
availableBlocks: IFormDynamicZoneBlock<Block>[];
blocksArray: Block[];
onAppendUpdate: (newBlock: Block) => void;
onRemoveUpdate: (index: number) => void;
onSwapUpdate: (firstIndex: number, secondIndex: number) => void;
onToggleUpdate: (index: number, opened: boolean) => void;
}

export function FormDynamicZone<T extends IBaseBlock>(
props: IFormDynamicZoneProps<T>,
export function FormDynamicZone<Block extends IBaseBlock>(
props: IFormDynamicZoneProps<Block>,
): ReactElement {
const {
actionLabels = defaultActionLabels,
availableBlocks,
blocksArray,
onUpdatedArray,
onAppendUpdate,
onRemoveUpdate,
onSwapUpdate,
onToggleUpdate,
} = props;

const blockOptions: IBaseBlockButton[] = availableBlocks.map((b) => b.button);

function renderBlock(block: T, index: number): ReactElement {
function renderBlock(block: IBaseBlockFull, index: number): ReactElement {
const correspondingType = availableBlocks.find(
(b) => b.block.blockType === block.blockType,
);
Expand All @@ -52,9 +63,7 @@ export function FormDynamicZone<T extends IBaseBlock>(
}

function onRemove(ref: IDynamicZoneBlockReference): void {
const newArray = [...blocksArray];
delete newArray[ref.index];
onUpdatedArray(newArray);
onRemoveUpdate(ref.index);
}

function onSwap(
Expand All @@ -63,21 +72,16 @@ export function FormDynamicZone<T extends IBaseBlock>(
): void {
const secondIndex = ref.index + (direction === 'up' ? -1 : 1);
if (secondIndex >= 0 && secondIndex < ref.arrayLength) {
const newArray = [...blocksArray];
// swap two elements in array
newArray[ref.index] = newArray.splice(
secondIndex,
1,
newArray[ref.index],
)[0];
onUpdatedArray(newArray);
onSwapUpdate(ref.index, secondIndex);
}
}

function onToggle(_block: T, index: number, opened: boolean): void {
const newArray = [...blocksArray];
newArray[index] = { ...newArray[index], opened };
onUpdatedArray(newArray);
function onToggle(
_block: IBaseBlockFull,
index: number,
opened: boolean,
): void {
onToggleUpdate(index, opened);
}

function isMoveDisabled(
Expand Down Expand Up @@ -121,23 +125,26 @@ export function FormDynamicZone<T extends IBaseBlock>(
`Could not append a block of blockType '${type} in given IFormDynamicZoneBlock[]'`,
);
}
const newArray = [...blocksArray];
newArray.push({
...(correspondingType.block as T),
const newBlock = {
...correspondingType.block,
id: crypto.randomUUID(),
});
onUpdatedArray(newArray);
} as Block;
onAppendUpdate(newBlock);
}

const blocksWithActions = blocksArray.map((b) => ({
...b,
blockActions: formDynamicZoneDefaultActions,
}));
const blocksWithOptionsAndActions: IBaseBlockFull[] = blocksArray
.filter(isNotNullNorEmpty)
.map((b) => ({
...b,
blockActions: formDynamicZoneDefaultActions,
...availableBlocks.find((o) => o.block.blockType === b.blockType)
?.blockOptions,
}));

return (
<DynamicZone<T>
<DynamicZone
blockOptions={blockOptions}
blocks={blocksWithActions}
blocks={blocksWithOptionsAndActions}
fluid
m={0}
onAppendBlock={onAppend}
Expand Down
2 changes: 2 additions & 0 deletions packages/haring-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ export type {
IThumbnailAction,
IThumbnailData,
IBaseBlock,
IBaseBlockOptions,
IBaseBlockFull,
IBaseBlockButton,
IBaseBlockType,
IFormDynamicZoneBlock,
Expand Down
22 changes: 11 additions & 11 deletions packages/haring-react/src/types/dynamic-zone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@ import type { ReactElement, ReactNode } from 'react';
export type IBaseBlockType = string;

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

// TODO: wip, try replacing the type of the blocks array with this rather than BaseBlock, will require a bunch of changes in FormDynamicZone and with <T>
export interface IBlock extends Record<string, unknown> {
blockType: IBaseBlockType;
readonly id: string;
opened: boolean;
export interface IBaseBlockOptions {
blockActions?: IAction<IDynamicZoneBlockReference>[];
blockFooter?: ReactNode;
blockHeader?: ReactNode;
}

export type IBaseBlockFull<Block extends IBaseBlock = IBaseBlock> = Block &
IBaseBlockOptions;

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

export interface IFormDynamicZoneBlock<T extends IBaseBlock> {
block: IOmitRespectIndexSignature<T, 'id'>;
export interface IFormDynamicZoneBlock<Block extends IBaseBlock> {
block: IOmitRespectIndexSignature<Block, 'id'>;
blockOptions: IBaseBlockOptions;
button: IBaseBlockButton;
renderFunc: (block: T, index: number) => ReactElement;
renderFunc: (block: IBaseBlockFull, index: number) => ReactElement;
}
Loading

0 comments on commit 674fe70

Please sign in to comment.