Skip to content

Commit

Permalink
test: update unit test, fix readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Sang-Sang33 committed Mar 9, 2023
1 parent 813528d commit 0d0229e
Show file tree
Hide file tree
Showing 13 changed files with 313 additions and 58 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.0.4 03/09
- add unit tests
- fix readme

## 0.0.3 03/08
- fix the style

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ npm install antd-form-json
yarn add antd-form-json
```

### [**antd-form-json-Storybook**](https://sang-sang33.github.io)
### [**Preview Storybook**](https://sang-sang33.github.io)

## Usage

```tsx
import { useState } from 'react';
import { FormJson } from 'antd-form-json';
import type { IFormItem, ETypes } from 'antd-form-json';
import { FormJson, ETypes } from 'antd-form-json';
import type { IFormItem } from 'antd-form-json';

const App = () => {
const [formStates, setFormStates] = useState<IFormItem[]>([
Expand Down
41 changes: 35 additions & 6 deletions docs/stories/FormJson.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { FormJsonStory, useFormStates, } from './FormJson';
import { useFormStates } from './FormJson';
import { ComponentMeta, ComponentStory } from '@storybook/react';
import React from 'react';
import { IFormJsonProps } from '../../src/index';
import { IFormJsonProps, FormJson, useJsonStates } from '../../src';
import { Input } from 'antd';

export default {
title: 'Example/FormJson',
component: FormJsonStory,
component: FormJson,
argTypes: {
spans: {
control: {
Expand All @@ -21,6 +22,18 @@ export default {
defaultValue: 16,
description: "Indent size in pixels of tree data."
},
containerClassName: {
control: {
type: 'string',
},
description: "The container class."
},
itemClassName: {
control: {
type: 'string',
},
description: "The class for each line's container."
},
formStates: {
control: false,
description: "The mutable state which has been controlled to the input form's value."
Expand Down Expand Up @@ -50,11 +63,27 @@ export default {
description: "A callback function, can be executed when the input element's value has been changed."
}
}
} as ComponentMeta<typeof FormJsonStory>;
} as ComponentMeta<typeof FormJson>;

const Template: ComponentStory<typeof FormJsonStory> = (args: Omit<IFormJsonProps, 'setFormStates' | 'formStates'>) => {
const Template: ComponentStory<typeof FormJson> = (args: Omit<IFormJsonProps, 'setFormStates' | 'formStates'>) => {
const stateProps = useFormStates();
return <FormJsonStory {...stateProps} {...args} />
const jsonStates = useJsonStates(stateProps.formStates)
return <div style={{display: 'flex'}}>
<div style={{width: '70%'}}>
<FormJson {...stateProps} {...args} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', width: '30%', backgroundColor: '#f6f7f8', padding: 16 }}>
<h3 style={{margin: 0, padding: 0}}>
The Json Result by the hook of useJsonStates
</h3>
<Input.TextArea
bordered={false}
style={{flex: 1, color: 'rgba(0, 0, 0, 0.88)', fontWeight: 500, resize: 'none'}}
value={JSON.stringify(jsonStates, null, 4)}
disabled
/>
</div>
</div>
};

const callbacksArgs = () => ({
Expand Down
18 changes: 1 addition & 17 deletions docs/stories/FormJson.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { FormJson, IFormJsonProps, useJsonStates } from '../../src/index';
import { useState } from 'react';
import { ETypes, IFormItem } from '../../src';
import React from 'react';
import { Input } from 'antd';


export function useFormStates() {
const [formStates, setFormStates] = useState<IFormItem[]>([
Expand Down Expand Up @@ -68,19 +66,5 @@ export function useFormStates() {
};
}

export const FormJsonStory = (props: IFormJsonProps) => {
const jsonStates = useJsonStates(props.formStates);
return <div style={{display: 'flex'}}>
<div style={{width: '70%'}}>
<FormJson {...props} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', width: '30%', backgroundColor: '#f6f7f8', padding: 16 }}>
<h3 style={{margin: 0, padding: 0}}>
The Json Result by the hook of useJsonStates
</h3>
<Input.TextArea bordered={false} style={{flex: 1, color: 'rgba(0, 0, 0, 0.88)', fontWeight: 500, resize: 'none'}} value={JSON.stringify(jsonStates, null, 4)} disabled />
</div>
</div>
}


2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "antd-form-json",
"version": "0.0.3",
"version": "0.0.4",
"description": "Generate Json data with the controlled form",
"main": "lib/index.es.js",
"module": "lib/index.es.js",
Expand Down
10 changes: 10 additions & 0 deletions src/FormJson/common/hooks/useFormActions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,14 @@ describe('useFormActions', () => {
expect(stateResult.current.formStates[1].value).toEqual("")
})

it('when the type change to "boolean", the type fo value should be boolean', () => {
const { result: stateResult } = renderHook(() => useFormStates())
const { result: actionsResult } = renderHook(() => useFormActions({setFormStates: stateResult.current.setFormStates}))
act(() => {
actionsResult.current.handleStateChange([1], 'type', ETypes.Boolean)
})
expect(stateResult.current.formStates[1].type).toEqual(ETypes.Boolean)
expect(stateResult.current.formStates[1].value).toEqual(true)
})

})
2 changes: 1 addition & 1 deletion src/FormJson/common/hooks/useJsonStates.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { act, renderHook } from '@testing-library/react';
import { renderHook } from '@testing-library/react';
import useJsonState from './useJsonStates';
import { uniqueId } from 'lodash-es';
import { ETypes } from '../constants';
Expand Down
130 changes: 130 additions & 0 deletions src/FormJson/components/FormLine/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { useFormStates } from '../../../../test/test-utils';
import { act, renderHook } from '@testing-library/react';
import useFormActions from '../../common/hooks/useFormActions';
import { screen, render, fireEvent } from '@testing-library/react';
import React from 'react';
import { omit } from "lodash-es";
import { vitest } from "vitest";
import FormLine from './index';

describe('Component FormLine', () => {
it("should be renderred by props", () => {
const { result: stateResult } = renderHook(() => useFormStates())
const { result: actionsResult } = renderHook(() => useFormActions({setFormStates: stateResult.current.setFormStates}))
const { container } = render(<FormLine
{...stateResult.current.formStates[0]}
{...actionsResult.current}
path={[0]}
parentType=""
isDeleteDisabled={true}
/>)
expect(container).toBeInTheDocument();
})
it("The CURD actions should be called.", async () => {
const { result: stateResult } = renderHook(() => useFormStates())
const { result: actionsResult } = renderHook(() => useFormActions({setFormStates: stateResult.current.setFormStates}))
const state = stateResult.current.formStates[1]
const onChange = vitest.fn();
const { container } = render(<FormLine
{...state}
{...omit(actionsResult.current, ['handleStateChange'])}
handleStateChange={(...args) => {
onChange();
actionsResult.current.handleStateChange(...args)
}
}
path={[1]}
parentType=""
isDeleteDisabled={true}
/>)
const input = screen.getByTestId(`line-${state.id}-key`)
fireEvent.change(input, { target: { value: 'mikasa-haha' } })

const typeSelect = screen.getByTestId(`line-${state.id}-type`).querySelector('.ant-select-selector')!;
await act(async () => {
await fireEvent.mouseDown(typeSelect);
vitest.useFakeTimers();
})
const options = document.querySelectorAll('.ant-select-item-option')
await act(async () => {
await fireEvent.click(options[0]);
vitest.useFakeTimers();
})
expect(container).toBeInTheDocument();
expect(onChange).toHaveBeenNthCalledWith(2)
expect(stateResult.current.formStates[1].name).toBe('mikasa-haha')
expect(stateResult.current.formStates[1].type).toBe('string')
expect(options.length).toBe(5)
})

it("The add actions should be called.", async () => {
const { result: stateResult } = renderHook(() => useFormStates())
const { result: actionsResult } = renderHook(() => useFormActions({setFormStates: stateResult.current.setFormStates}))
const state = stateResult.current.formStates[1]
const onAddSibling = vitest.fn();
const { container } = render(<FormLine
{...state}
{...omit(actionsResult.current, ['handleAddSibling'])}
handleAddSibling={(...args) => {
onAddSibling();
actionsResult.current.handleAddSibling(...args)
}}
path={[1]}
parentType=""
isDeleteDisabled={true}
/>)

const addButton = screen.getByTestId(`line-${state.id}-add`)
fireEvent.click(addButton)
expect(container).toBeInTheDocument()
expect(addButton).toBeInTheDocument()
expect(onAddSibling).toHaveBeenCalled()
expect(stateResult.current.formStates.length).toBe(4)
})

it("The delete actions should be called.", async () => {
const { result: stateResult } = renderHook(() => useFormStates())
const { result: actionsResult } = renderHook(() => useFormActions({setFormStates: stateResult.current.setFormStates}))
const state = stateResult.current.formStates[1]
const onDeleteItem = vitest.fn();
const { container } = render(<FormLine
{...state}
{...omit(actionsResult.current, ['handleDeleteItem'])}
handleDeleteItem={(...args) => {
onDeleteItem();
actionsResult.current.handleDeleteItem(...args)
}}
path={[1]}
parentType=""
isDeleteDisabled={false}
/>)

const addButton = screen.getByTestId(`line-${state.id}-delete`)
fireEvent.click(addButton)
expect(container).toBeInTheDocument()
expect(addButton).toBeInTheDocument()
expect(onDeleteItem).toHaveBeenCalled()
expect(stateResult.current.formStates.length).toBe(2)
})

it("The add actions should change when the type is 'object' or 'array'.", async () => {
const { result: stateResult } = renderHook(() => useFormStates())
const { result: actionsResult } = renderHook(() => useFormActions({setFormStates: stateResult.current.setFormStates}))
const state = stateResult.current.formStates[0]
const onAddSibling = vitest.fn();
const { container } = render(<FormLine
{...state}
{...omit(actionsResult.current, ['handleAddSibling'])}
handleAddSibling={(...args) => {
onAddSibling();
actionsResult.current.handleAddSibling(...args)
}}
path={[0]}
parentType=""
isDeleteDisabled={true}
/>)
const addButton = screen.getByTestId(`line-${state.id}-menu-children`)
expect(addButton).toBeInTheDocument()
expect(container).toBeInTheDocument()
})
})
36 changes: 21 additions & 15 deletions src/FormJson/components/FormLine/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@ interface IProps extends IFormItem, ReturnFormACtions {
itemClassName?: string;
}

function FormLine({
name,
value,
type,
path,
indent = 12,
spans = [8, 5, 8, 3],
parentType,
isDeleteDisabled,
function FormLine({
name,
value,
id,
type,
path,
indent = 12,
spans = [8, 5, 8, 3],
parentType,
isDeleteDisabled,
itemClassName = '',
handleAddSibling,
handleAddChildren,
handleDeleteItem,
handleStateChange
handleAddSibling,
handleAddChildren,
handleDeleteItem,
handleStateChange
}: IProps) {
const isComplexType = useMemo(() => isComplexTypeFn(type as ETypes), [type]);
const shouldKeyDisabled = parentType === ETypes.Array;
Expand Down Expand Up @@ -65,6 +66,7 @@ function FormLine({
<Row className={itemClassName} style={{margin: 0, padding: '8px 0'}} align={'middle'} gutter={16}>
<Col span={spans[0]} style={{ paddingLeft: (path.length - 1) * indent }}>
<Input
data-testid={`line-${id}-key`}
placeholder={shouldKeyDisabled ? '' : '请输入json的key'}
value={name}
onChange={(e) => {
Expand All @@ -75,6 +77,7 @@ function FormLine({
</Col>
<Col span={spans[1]}>
<Select
data-testid={`line-${id}-type`}
value={type}
style={{width: '100%'}}
options={TYPE_OPTIONS}
Expand All @@ -84,16 +87,17 @@ function FormLine({
/>
</Col>
<Col span={spans[2]}>
<ValueInputByType value={value} type={type} path={path} handleStateChange={handleStateChange} />
<ValueInputByType value={value} type={type} path={path} id={id} handleStateChange={handleStateChange} />
</Col>
<Col span={spans[3]}>
{isComplexType ? (
<Dropdown menu={{ items }}>
<Dropdown menu={{ items }} data-testid={`line-${id}-menu`}>
<Button
shape={'circle'}
type={'link'}
size={'small'}
icon={<PlusOutlined />}
data-testid={`line-${id}-menu-children`}
/>
</Dropdown>
) : (
Expand All @@ -105,12 +109,14 @@ function FormLine({
type={'link'}
size={'small'}
icon={<PlusOutlined />}
data-testid={`line-${id}-add`}
/>
)}

<Button
disabled={isDeleteDisabled}
style={{ marginLeft: 8 }}
data-testid={`line-${id}-delete`}
onClick={() => {
if (isDeleteDisabled) return;
handleDeleteItem(path);
Expand Down
Loading

0 comments on commit 0d0229e

Please sign in to comment.