Skip to content

Commit

Permalink
feature: Reimplement and improve rjsf-team#1719 to Add array field copy
Browse files Browse the repository at this point in the history
Fixes rjsf-team#1261 and rjsf-team#1712 by adding copy array item capability as well as global `UiSchema` options
- In `@rjsf/utils`, updated types and functions to support array field copy AND global options in the `UiSchema` as follows:
- Updated the `ArrayFieldTemplateItemType` to add support for copying array items
  - Added a new `TranslatableString` enums `CopyButton` and `InvalidObjectField` that localizes the information extracted from `ObjectField` as markdown
  - Refactored `UIOptionsBaseType` to extract the `addable`, `orderable`, `removable`, `label` and `duplicateKeySuffixSeparator` into a new `GlobalUISchemaOptions` type that adds `copyable`
    - Extended `UIOptionsBaseType` from `GlobalUISchemaOptions`
    - In `UiSchema` added a new optional `ui:globalOptions` prop of type `GlobalUISchemaOptions` and a new `UI_GLOBAL_OPTIONS_KEY` constant
    - Added a new optional prop `globalUiOptions` object of type `GlobalUISchemaOptions` in `Registry` as well as `CopyButton` in `ButtonTemplates`
  - Updated `getUiOptions()` and `getDisplayLabel()` (and its `SchemaUtilsType` counterpart) to take an optional `GlobalUISchemaOptions` parameter that is used to include global options into the returned `uiOptions`
- In `@rjsf/core`, added support for array field copy and global options in the `UiSchema` as follows:
  - Updated `ArrayField` to handle global UI Options by passing in `registry.globalUiOptions` into `getUiOptions()` and by exposing the new `hasCopy` flag and `onCopyIndexClick` callback
  - Updated `ObjectField` to handle global UI Options for `duplicateKeySuffixSeparator` and also added support for the new `TranslatableString.InvalidObjectField` translation into a `Markdown`
  - Updated `SchemaField` to handle global UI Options for `label`
  - Added a new `CopyButton` implementation that was registered in the `ButtonTemplates`
  - Updated `ArrayFieldItemTemplate` to render the `CopyButton` when `hasCopy` is true, calling `onCopyIndexClick` on click
  - Updated `Form` to extract the `ui:globalOptions` from the `uiSchema` and set it into the `registry` as `globalUiOptions`
  - Updated tests to verify all the new functionality
- In all the themes, added support for array field copy as follows:
  - Added a new `CopyButton` implementation that was registered in the `ButtonTemplates`
  - Updated `ArrayFieldItemTemplate` to render the `CopyButton` when `hasCopy` is true, calling `onCopyIndexClick` on click
  - Updated the Array tests to verify that copy shows up when `copyable` is true
- In `@rjsf/antd` and `@rjsf/semantic-ui` updated the styles to support the additional button in `ArrayFieldItemTemplate`
- In `@rjsf/fluent-ui`, fixed some bad style errors in the console by removing the `;` at the end of the `fontFamily` custom styles
- In `@rjsf/semantic-ui`, removed some bad property warnings by changing the `inverted` prop from `false` to `'false'`
- In `@rjsf/docs`, updated the documentation for all the copy feature and global `UiSchema` options type updates
  - Also replaced all `js(x)` code blocks with `ts(x)` code blocks to be complete
- Updated the `CHANGELOG.md` accordingly
  • Loading branch information
heath-freenome committed Mar 15, 2023
1 parent f74c733 commit 1e82673
Show file tree
Hide file tree
Showing 75 changed files with 1,353 additions and 240 deletions.
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,57 @@ should change the heading of the (upcoming) version to include a major version b

# 5.3.0

## @rjsf/antd

- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/bootstrap-4

- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/chakra-ui

- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/core

- `Reset` function added for `Programmatically Reset` action. `Reset` function will reset form data and validation errors. Form data will set to default values.
- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)
- Also added the missing translation for the `InvalidObjectField` error in `ObjectField`
- Added support for the handling of the global UiSchema options in `Form`, `ArrayField`, `ObjectField` and `SchemaField`

## @rjsf/fluent-ui

- Fix RadioWidget's onChange return value.
- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/material-ui

- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/mui

- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/semantic-ui

- Added support to make a copy of an array item directly after the item selected to be copied (feature is off by default), fixing [#1261](https://github.com/rjsf-team/react-jsonschema-form/issues/1261) and [#1712](https://github.com/rjsf-team/react-jsonschema-form/issues/1712)

## @rjsf/utils

- Added a new `TranslatableString` enums `CopyButton` and `InvalidObjectField` that localizes the information extracted from `ObjectField` as markdown
- Updated the `ArrayFieldTemplateItemType` to add support for copying array items
- Refactored `UIOptionsBaseType` to extract the `addable`, `orderable`, `removable`, `label` and `duplicateKeySuffixSeparator` into a new `GlobalUISchemaOptions` type that adds `copyable`
- Extended `UIOptionsBaseType` from `GlobalUISchemaOptions`
- In `UiSchema` added a new optional `ui:globalOptions` prop of type `GlobalUISchemaOptions` and a new `UI_GLOBAL_OPTIONS_KEY` constant
- Added a new optional prop `globalUiOptions` object of type `GlobalUISchemaOptions` in `Registry` as well as `CopyButton` in `ButtonTemplates`
- Updated `getUiOptions()` and `getDisplayLabel()` (and its `SchemaUtilsType` counterpart) to take an optional `GlobalUISchemaOptions` parameter that is used to include global options into the returned `uiOptions`

## Dev / docs / playground

- Added `Programmatically Reset` button to clear states which are form data and validation errors.
- Updated the documentation for `custom-templates`, `internals`, `uiSchema`, `utility-functions` and `arrays` for the new copy array feature as well as the global UI Schema options support
- Updated the `arrays` example to add examples for the new `copyable` feature

# 5.2.1

Expand Down
15 changes: 13 additions & 2 deletions packages/antd/src/templates/ArrayFieldItemTemplate/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const BTN_GRP_STYLE = {
};

const BTN_STYLE = {
width: 'calc(100% / 3)',
width: 'calc(100% / 4)',
};

/** The `ArrayFieldItemTemplate` component is the template used to render an items of an array.
Expand All @@ -23,18 +23,20 @@ export default function ArrayFieldItemTemplate<
const {
children,
disabled,
hasCopy,
hasMoveDown,
hasMoveUp,
hasRemove,
hasToolbar,
index,
onCopyIndexClick,
onDropIndexClick,
onReorderClick,
readonly,
registry,
uiSchema,
} = props;
const { MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates;
const { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates;
const { rowGutter = 24, toolbarAlign = 'top' } = registry.formContext;

return (
Expand Down Expand Up @@ -62,6 +64,15 @@ export default function ArrayFieldItemTemplate<
registry={registry}
/>
)}
{hasCopy && (
<CopyButton
disabled={disabled || readonly}
onClick={onCopyIndexClick(index)}
style={BTN_STYLE}
uiSchema={uiSchema}
registry={registry}
/>
)}
{hasRemove && (
<RemoveButton
disabled={disabled || readonly}
Expand Down
10 changes: 10 additions & 0 deletions packages/antd/src/templates/IconButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Button, { ButtonProps, ButtonType } from 'antd/lib/button';
import ArrowDownOutlined from '@ant-design/icons/ArrowDownOutlined';
import ArrowUpOutlined from '@ant-design/icons/ArrowUpOutlined';
import CopyOutlined from '@ant-design/icons/CopyOutlined';
import DeleteOutlined from '@ant-design/icons/DeleteOutlined';
import PlusCircleOutlined from '@ant-design/icons/PlusCircleOutlined';
import {
Expand Down Expand Up @@ -43,6 +44,15 @@ export function AddButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
);
}

export function CopyButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
props: AntdIconButtonProps<T, S, F>
) {
const {
registry: { translateString },
} = props;
return <IconButton title={translateString(TranslatableString.CopyButton)} {...props} icon={<CopyOutlined />} />;
}

export function MoveDownButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
props: AntdIconButtonProps<T, S, F>
) {
Expand Down
3 changes: 2 additions & 1 deletion packages/antd/src/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ArrayFieldTemplate from './ArrayFieldTemplate';
import BaseInputTemplate from './BaseInputTemplate';
import DescriptionField from './DescriptionField';
import ErrorList from './ErrorList';
import { AddButton, MoveDownButton, MoveUpButton, RemoveButton } from './IconButton';
import { AddButton, CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from './IconButton';
import FieldErrorTemplate from './FieldErrorTemplate';
import FieldTemplate from './FieldTemplate';
import ObjectFieldTemplate from './ObjectFieldTemplate';
Expand All @@ -24,6 +24,7 @@ export function generateTemplates<
BaseInputTemplate,
ButtonTemplates: {
AddButton,
CopyButton,
MoveDownButton,
MoveUpButton,
RemoveButton,
Expand Down
9 changes: 7 additions & 2 deletions packages/antd/test/Array.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import renderer from 'react-test-renderer';
import { RJSFSchema, ErrorSchema } from '@rjsf/utils';
import { RJSFSchema, ErrorSchema, UiSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';

import '../__mocks__/matchMedia.mock';
Expand Down Expand Up @@ -62,7 +62,12 @@ describe('array fields', () => {
type: 'string',
},
};
const tree = renderer.create(<Form schema={schema} validator={validator} formData={['a', 'b']} />).toJSON();
const uiSchema: UiSchema = {
'ui:options': { copyable: true },
};
const tree = renderer
.create(<Form schema={schema} uiSchema={uiSchema} validator={validator} formData={['a', 'b']} />)
.toJSON();
expect(tree).toMatchSnapshot();
});

Expand Down
76 changes: 70 additions & 6 deletions packages/antd/test/__snapshots__/Array.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ exports[`array fields array icons 1`] = `
onClick={[Function]}
style={
Object {
"width": "calc(100% / 3)",
"width": "calc(100% / 4)",
}
}
title="Move up"
Expand Down Expand Up @@ -271,7 +271,7 @@ exports[`array fields array icons 1`] = `
onClick={[Function]}
style={
Object {
"width": "calc(100% / 3)",
"width": "calc(100% / 4)",
}
}
title="Move down"
Expand All @@ -297,13 +297,45 @@ exports[`array fields array icons 1`] = `
</svg>
</span>
</button>
<button
className="ant-btn ant-btn-default ant-btn-icon-only"
disabled={false}
onClick={[Function]}
style={
Object {
"width": "calc(100% / 4)",
}
}
title="Copy"
type="button"
>
<span
aria-label="copy"
className="anticon anticon-copy"
role="img"
>
<svg
aria-hidden="true"
data-icon="copy"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"
/>
</svg>
</span>
</button>
<button
className="ant-btn ant-btn-primary ant-btn-icon-only ant-btn-dangerous"
disabled={false}
onClick={[Function]}
style={
Object {
"width": "calc(100% / 3)",
"width": "calc(100% / 4)",
}
}
title="Remove"
Expand Down Expand Up @@ -430,7 +462,7 @@ exports[`array fields array icons 1`] = `
onClick={[Function]}
style={
Object {
"width": "calc(100% / 3)",
"width": "calc(100% / 4)",
}
}
title="Move up"
Expand Down Expand Up @@ -462,7 +494,7 @@ exports[`array fields array icons 1`] = `
onClick={[Function]}
style={
Object {
"width": "calc(100% / 3)",
"width": "calc(100% / 4)",
}
}
title="Move down"
Expand All @@ -488,13 +520,45 @@ exports[`array fields array icons 1`] = `
</svg>
</span>
</button>
<button
className="ant-btn ant-btn-default ant-btn-icon-only"
disabled={false}
onClick={[Function]}
style={
Object {
"width": "calc(100% / 4)",
}
}
title="Copy"
type="button"
>
<span
aria-label="copy"
className="anticon anticon-copy"
role="img"
>
<svg
aria-hidden="true"
data-icon="copy"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"
/>
</svg>
</span>
</button>
<button
className="ant-btn ant-btn-primary ant-btn-icon-only ant-btn-dangerous"
disabled={false}
onClick={[Function]}
style={
Object {
"width": "calc(100% / 3)",
"width": "calc(100% / 4)",
}
}
title="Remove"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ export default function ArrayFieldItemTemplate<
children,
disabled,
hasToolbar,
hasCopy,
hasMoveDown,
hasMoveUp,
hasRemove,
index,
onCopyIndexClick,
onDropIndexClick,
onReorderClick,
readonly,
registry,
uiSchema,
} = props;
const { MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates;
const { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } = registry.templates.ButtonTemplates;
const btnStyle: CSSProperties = {
flex: 1,
paddingLeft: 6,
Expand Down Expand Up @@ -61,6 +63,17 @@ export default function ArrayFieldItemTemplate<
/>
</div>
)}
{hasCopy && (
<div className='m-0 p-0'>
<CopyButton
style={btnStyle}
disabled={disabled || readonly}
onClick={onCopyIndexClick(index)}
uiSchema={uiSchema}
registry={registry}
/>
</div>
)}
{hasRemove && (
<div className='m-0 p-0'>
<RemoveButton
Expand Down
10 changes: 10 additions & 0 deletions packages/bootstrap-4/src/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FormContextType, IconButtonProps, RJSFSchema, StrictRJSFSchema, TranslatableString } from '@rjsf/utils';
import Button, { ButtonProps } from 'react-bootstrap/Button';
import { IoIosCopy } from '@react-icons/all-files/io/IoIosCopy';
import { IoIosRemove } from '@react-icons/all-files/io/IoIosRemove';
import { AiOutlineArrowUp } from '@react-icons/all-files/ai/AiOutlineArrowUp';
import { AiOutlineArrowDown } from '@react-icons/all-files/ai/AiOutlineArrowDown';
Expand All @@ -15,6 +16,15 @@ export default function IconButton<T = any, S extends StrictRJSFSchema = RJSFSch
);
}

export function CopyButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
props: IconButtonProps<T, S, F>
) {
const {
registry: { translateString },
} = props;
return <IconButton title={translateString(TranslatableString.CopyButton)} {...props} icon={<IoIosCopy />} />;
}

export function MoveDownButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
props: IconButtonProps<T, S, F>
) {
Expand Down
3 changes: 2 additions & 1 deletion packages/bootstrap-4/src/Templates/Templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import ArrayFieldTemplate from '../ArrayFieldTemplate';
import BaseInputTemplate from '../BaseInputTemplate/BaseInputTemplate';
import DescriptionField from '../DescriptionField';
import ErrorList from '../ErrorList';
import { MoveDownButton, MoveUpButton, RemoveButton } from '../IconButton';
import { CopyButton, MoveDownButton, MoveUpButton, RemoveButton } from '../IconButton';
import FieldErrorTemplate from '../FieldErrorTemplate';
import FieldHelpTemplate from '../FieldHelpTemplate';
import FieldTemplate from '../FieldTemplate';
Expand All @@ -25,6 +25,7 @@ export function generateTemplates<
BaseInputTemplate,
ButtonTemplates: {
AddButton,
CopyButton,
MoveDownButton,
MoveUpButton,
RemoveButton,
Expand Down
9 changes: 7 additions & 2 deletions packages/bootstrap-4/test/Array.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RJSFSchema, ErrorSchema } from '@rjsf/utils';
import { RJSFSchema, ErrorSchema, UiSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
import renderer from 'react-test-renderer';

Expand Down Expand Up @@ -49,7 +49,12 @@ describe('array fields', () => {
type: 'string',
},
};
const tree = renderer.create(<Form schema={schema} validator={validator} formData={['a', 'b']} />).toJSON();
const uiSchema: UiSchema = {
'ui:options': { copyable: true },
};
const tree = renderer
.create(<Form schema={schema} uiSchema={uiSchema} validator={validator} formData={['a', 'b']} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
test('no errors', () => {
Expand Down
Loading

0 comments on commit 1e82673

Please sign in to comment.