Skip to content

Commit

Permalink
feat: wip DynamicZoneBlock style, opened state, header
Browse files Browse the repository at this point in the history
  • Loading branch information
Quentin Le Caignec committed Aug 20, 2024
1 parent f5777e1 commit f1f7958
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IBaseBlock } from '@smile/haring-react/src/Form/DynamicZone/DynamicZone';
import type { IBaseBlock } from '@smile/haring-react';
import type { ReactElement } from 'react';

import { Flex } from '@mantine/core';
Expand All @@ -7,6 +7,7 @@ import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';

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

Expand All @@ -25,18 +26,19 @@ export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
});
const watched = useWatch({ control, name: dynamicZoneName });

function newBlock(field: IFormBlock, index: number): ReactElement {
function newBlock(block: IFormBlock, index: number): ReactElement {
return (
<input
key={field.id}
key={block.id}
{...register(`${dynamicZoneName}.${index}.value` as const)}
/>
);
}

function onAppend(): void {
function onAppend(blockType: string): void {
append({
fieldName: `appended-${fields.length}`,
fieldName: `${blockType}-${fields.length}`,
type: blockType,
value: '',
});
}
Expand All @@ -48,9 +50,6 @@ export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
return (
<div>
<Flex>
<button onClick={onAppend} type="button">
add
</button>
<button onClick={() => remove(0)} type="button">
remove first
</button>
Expand All @@ -65,8 +64,10 @@ export function FormDynamicZone(props: IFormDynamicZoneProps): ReactElement {
<form onSubmit={handleSubmit(onSubmit)}>
form container, {dynamicZoneName}: {JSON.stringify(watched)}
<DynamicZone<IFormBlock>

Check failure on line 66 in packages/haring-react-hook-form/src/Components/FormDynamicZone/FormDynamicZone.tsx

View workflow job for this annotation

GitHub Actions / test

Property 'onToggleBlock' is missing in type '{ blockOptions: string[]; blocks: IFormBlock[]; onAppendBlock: (blockType: string) => void; onRenderBlockContent: (block: IFormBlock, index: number) => ReactElement<any, string | JSXElementConstructor<...>>; }' but required in type 'IDynamicZoneProps<IFormBlock>'.
blockOptions={['default']}
blocks={fields as IFormBlock[]}
onCreatingBlock={newBlock}
onAppendBlock={onAppend}
onRenderBlockContent={newBlock}
/>
<input type="submit" />
</form>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react';

import { action } from '@storybook/addon-actions';

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

const meta = {
Expand All @@ -13,11 +15,14 @@ type IStory = StoryObj<typeof meta>;

export const DynamicZone: IStory = {
args: {
blockOptions: ['default'],
blocks: [
{ id: '1', value: 'initial' },
{ id: '2', value: 'initial' },
{ id: '3', value: 'initial' },
{ id: '1', opened: false, value: 'initial' },
{ id: '2', opened: true, value: 'initial' },
{ id: '3', opened: false, value: 'initial' },
],
onCreatingBlock: (_b, index) => <input key={index} />,
onAppendBlock: action('onAppendBlock, type'),
onRenderBlockContent: (_b, index) => <input key={index} />,
onToggleBlock: action('onToggleBlock'),
},
};
69 changes: 52 additions & 17 deletions packages/haring-react/src/Form/DynamicZone/DynamicZone.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,90 @@
import type { ContainerProps, GroupProps, StackProps } from '@mantine/core';
import type { IBaseBlock } from '../../types';
import type {
ButtonProps,
CardProps,
CardSectionProps,
ContainerProps,
GroupProps,
StackProps,
} from '@mantine/core';
import type { ReactElement } from 'react';

import { Container, Group, Stack } from '@mantine/core';
import { Button, Container, Group, Stack } from '@mantine/core';

import classes from './DynamicZone.module.css';
import { DynamicZoneBlock } from './DynamicZoneBlock/DynamicZoneBlock';

export interface IBaseBlock extends Record<string, unknown> {
readonly id: string;
}

export interface IDynamicZoneProps<Block extends IBaseBlock> {
blockCardProps?: {
cardProps?: CardProps;
cardSectionProps?: CardSectionProps;
};
blockOptions: string[];
blocks: Block[];
blocksStackProps?: StackProps;
buttonProps?: ButtonProps;
buttonsGroupProps?: GroupProps;
onCreatingBlock: (block: Block, index: number) => ReactElement;
onAppendBlock: (blockType: string) => void;
onRenderBlockContent: (block: Block, index: number) => ReactElement;
onToggleBlock: (opened: boolean) => void;
rootContainerProps?: ContainerProps;
}

export function DynamicZone<Block extends IBaseBlock>(
props: IDynamicZoneProps<Block>,
): ReactElement {
const {
blocks,
blockCardProps,
blockOptions,
blocksStackProps,
buttonProps,
buttonsGroupProps,
onCreatingBlock,
blocks,
onAppendBlock,
onRenderBlockContent,
onToggleBlock,
rootContainerProps,
} = props;

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

return (
<Container
className={classes.container}
fluid
p={10}
p="sm"
{...rootContainerProps}
>
<Stack className={classes.blocksContainer} {...blocksStackProps}>
<Stack className={classes.blocksContainer} gap="sm" {...blocksStackProps}>
{blocks.map((block, index) => (
<DynamicZoneBlock key={block.id}>
{onCreatingBlock(block, index)}
<DynamicZoneBlock
cardSectionProps={blockCardProps?.cardSectionProps}
{...blockCardProps?.cardProps}
key={block.id}
onToggle={onToggleBlock}
opened={block.opened}
>
{onRenderBlockContent(block, index)}
</DynamicZoneBlock>
))}
</Stack>
<Group
className={classes.buttonsContainer}
mt={20}
mt="lg"
{...buttonsGroupProps}
>
<button type="button">A</button>
<button type="button">B</button>
<button type="button">C</button>
{blockOptions.map((blockType) => (
<Button
type="button"
{...buttonProps}
key={`button-${blockType}`}
onClick={() => onAddBlock(blockType)}
>
{blockType}
</Button>
))}
</Group>
</Container>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
.container {
/* WIP */
border: 1px red dashed;
}

.header {
cursor: pointer;

&:hover {
filter: contrast(40%);
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
import type { ContainerProps } from '@mantine/core';
import type { CardProps, CardSectionProps } from '@mantine/core';
import type { ReactElement, ReactNode } from 'react';

import { Container } from '@mantine/core';
import { Card, Collapse, Container, Group } from '@mantine/core';
import { CaretDown, CaretUp } from '@phosphor-icons/react';

import classes from './DynamicZoneBlock.module.css';

export interface IDynamicZoneBlockProps {
export interface IDynamicZoneBlockProps extends CardProps {
cardSectionProps?: CardSectionProps;
children: ReactNode;
containerProps?: ContainerProps;
onToggle: (opened: boolean) => void;
opened: boolean;
}

export function DynamicZoneBlock(props: IDynamicZoneBlockProps): ReactElement {
const { children, containerProps } = props;
const { cardSectionProps, children, onToggle, opened, ...cardProps } = props;
return (
<Container
<Card
bg="white"
className={classes.container}
fluid
m={0}
p={10}
{...containerProps}
radius="md"
shadow="sm"
withBorder
{...cardProps}
>
{children}
</Container>
<Card.Section
className={classes.header}
p="sm"
withBorder={opened}
{...cardSectionProps}
onClick={() => onToggle(!opened)}
role="button"
>
<Group justify="space-between">
<Group>
<span>icon</span>
<span>title</span>
</Group>
{opened ? <CaretDown /> : <CaretUp />}
</Group>
</Card.Section>
<Collapse {...cardSectionProps} in={opened}>
<Container fluid p="xs">
{children}
</Container>
</Collapse>
<Card.Section p={0} {...cardSectionProps} />
</Card>
);
}
7 changes: 6 additions & 1 deletion packages/haring-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,9 @@ export {
setChildrenToTree,
} from './helpers';
// type exports
export type { IThumbnail, IThumbnailAction, IThumbnailData } from './types';
export type {
IThumbnail,
IThumbnailAction,
IThumbnailData,
IBaseBlock,
} from './types';
4 changes: 4 additions & 0 deletions packages/haring-react/src/types/dynamic-zone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IBaseBlock extends Record<string, unknown> {
id: string;
opened: boolean;
}
1 change: 1 addition & 0 deletions packages/haring-react/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './thumbnail';
export * from './dynamic-zone';

0 comments on commit f1f7958

Please sign in to comment.