Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG_v6.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ should change the heading of the (upcoming) version to include a major version b
- `findFieldInSchema<T = undefined, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, path: string | string[], schema: S, formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): FoundFieldType<S>`: Finds the field specified by the `path` within the root or recursed `schema`
- `findSelectedOptionInXxxOf<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, schema: S, fallbackField: string,xxx: 'anyOf' | 'oneOf', formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S | undefined`: Finds the option that matches the selector field in the `schema` or undefined if nothing is selected
- `getFromSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, schema: S, path: string | string[], defaultValue: T | S, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): T | S`: Helper that acts like lodash's `get` but additionally retrieves `$ref`s as needed to get the path for schemas

- Exported a `browser` version of the libraries with a browser-safe version of `getTestIds()`

## @rjsf/validator-ajv6

Expand All @@ -91,6 +91,7 @@ should change the heading of the (upcoming) version to include a major version b
- Updated the `custom-templates.md` documentation for the changes to the `ArrayFieldTemplateItem` and add the two new templates
- Updated the `utility-functions.md` documentation to add the `buttonId()` function
- Added the `v6.x upgrade guide.md` documentation
- Updated the `playground` to add a `Layout Grid` example and made the selected example now be part of the shared export

# 6.0.0-alpha.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export default function LayoutMultiSchemaField<
errorSchema,
hideError = false,
} = props;
const { widgets, schemaUtils } = registry;
const { widgets, schemaUtils, globalUiOptions } = registry;
const [enumOptions, setEnumOptions] = useState(computeEnumOptions(schema, options, schemaUtils, uiSchema, formData)!);
const id = get(idSchema, ID_KEY);
const discriminator = getDiscriminatorFieldFromSchema(schema);
Expand All @@ -128,6 +128,7 @@ export default function LayoutMultiSchemaField<
const {
widget = discriminator ? 'radio' : 'select',
title = '',
placeholder = '',
optionsSchemaSelector: selectorField = discriminator,
hideError: uiSchemaHideError,
...uiOptions
Expand All @@ -148,6 +149,7 @@ export default function LayoutMultiSchemaField<

const rawErrors = get(errorSchema, [ERRORS_KEY], []) as string[];
const fieldErrorSchema = omit(errorSchema, [ERRORS_KEY]);
const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);

/** Callback function that updates the selected option and adjusts the form data based on the structure of the new
* option, calling the `onChange` callback with the adjusted formData.
Expand All @@ -167,7 +169,7 @@ export default function LayoutMultiSchemaField<
if (newFormData) {
set(newFormData, selectorField, opt);
}
onChange(newFormData);
onChange(newFormData, undefined, id);
};

// filtering the options based on the type of widget because `selectField` does not recognize the `convertOther` prop
Expand All @@ -190,8 +192,9 @@ export default function LayoutMultiSchemaField<
multiple={false}
rawErrors={rawErrors}
hideError={hideFieldError}
hideLabel={!displayLabel}
errorSchema={fieldErrorSchema}
placeholder=''
placeholder={placeholder}
onChange={onOptionChange}
onBlur={onBlur}
onFocus={onFocus}
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/components/templates/GridTemplate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import { GridTemplateProps } from '@rjsf/utils';
*/
export default function GridTemplate(props: GridTemplateProps) {
const { children, column, className, ...rest } = props;
const classNames = column ? className : `row ${className}`;
return (
<div className={classNames} {...rest}>
<div className={className} {...rest}>
{children}
</div>
);
Expand Down
7 changes: 2 additions & 5 deletions packages/core/test/LayoutGridField.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,7 @@ describe('LayoutGridField', () => {
render(<LayoutGridField {...props} />);
// Renders an outer grid row
const row = screen.getByTestId(LayoutGridField.TEST_IDS.row);
expect(row).toHaveClass('row');
expect(row).toBeInTheDocument();
// Renders 2 fields in the row
const fields = within(row).getAllByTestId(LayoutGridField.TEST_IDS.field);
expect(fields).toHaveLength(GRID_CHILDREN.length);
Expand All @@ -1407,7 +1407,7 @@ describe('LayoutGridField', () => {
render(<LayoutGridField {...props} />);
// Renders an outer grid row item with width 6
const row = screen.getByTestId(LayoutGridField.TEST_IDS.row);
expect(row).toHaveClass(`row ${ColumnWidth6}`);
expect(row).toHaveClass(ColumnWidth6);
// Renders 2 fields in the row
const fields = within(row).getAllByTestId(LayoutGridField.TEST_IDS.field);
expect(fields).toHaveLength(GRID_CHILDREN.length);
Expand All @@ -1428,7 +1428,6 @@ describe('LayoutGridField', () => {
// Renders an outer grid item with width 6, but not a row
const col = screen.getByTestId(LayoutGridField.TEST_IDS.col);
expect(col).toHaveClass(ColumnWidth6);
expect(col).not.toHaveClass('row');
// Renders 2 fields in the column
const fields = within(col).getAllByTestId(LayoutGridField.TEST_IDS.field);
expect(fields).toHaveLength(GRID_CHILDREN.length);
Expand All @@ -1451,13 +1450,11 @@ describe('LayoutGridField', () => {
expect(cols).toHaveLength(GRID_CHILDREN.length);
// First column is a grid item with width 6
expect(cols[0]).toHaveClass(ColumnWidth6);
expect(cols[0]).not.toHaveClass('row');
// Renders first field in the first column
let field = within(cols[0]).getByTestId(LayoutGridField.TEST_IDS.field);
expect(field).toHaveTextContent(stringifyProps(getExpectedPropsForField(props, GRID_CHILDREN[0])));
// First column is a grid item with 6
expect(cols[1]).toHaveClass(ColumnWidth6);
expect(cols[1]).not.toHaveClass('row');
// Renders second field in the second column
field = within(cols[1]).getByTestId(LayoutGridField.TEST_IDS.field);
expect(field).toHaveTextContent(stringifyProps(getExpectedPropsForField(props, GRID_CHILDREN[1])));
Expand Down
16 changes: 10 additions & 6 deletions packages/core/test/LayoutMultiSchemaField.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ describe('LayoutMultiSchemaField', () => {
await user.click(input);

// OnChange was called with the correct event
expect(props.onChange).toHaveBeenCalledWith({ [selectorField]: '2' });
expect(props.onChange).toHaveBeenCalledWith({ [selectorField]: '2' }, undefined, DEFAULT_ID);

// Rerender to simulate the onChange updating the value
const newFormData = { [selectorField]: SIMPLE_ONEOF_OPTIONS[1].value };
Expand Down Expand Up @@ -347,10 +347,14 @@ describe('LayoutMultiSchemaField', () => {
props.formData
);
await waitFor(() => {
expect(props.onChange).toHaveBeenCalledWith({
...props.registry.schemaUtils.getDefaultFormState(retrievedOptions[0], sanitizedFormData),
[selectorField]: 'first_option',
});
expect(props.onChange).toHaveBeenCalledWith(
{
...props.registry.schemaUtils.getDefaultFormState(retrievedOptions[0], sanitizedFormData),
[selectorField]: 'first_option',
},
undefined,
DEFAULT_ID
);
});
});
test('custom selector field, ui:hideError false, props.hideError true, required true, autofocus true', async () => {
Expand Down Expand Up @@ -404,7 +408,7 @@ describe('LayoutMultiSchemaField', () => {
await user.selectOptions(button, '');

// OnChange was called with the correct event
expect(props.onChange).toHaveBeenCalledWith(undefined);
expect(props.onChange).toHaveBeenCalledWith(undefined, undefined, DEFAULT_ID);
});
test('no options for radio widget, ui:hideError true, props.hideError false, no errors to hide', () => {
const props = getProps({ options: [], uiSchema: { 'ui:hideError': true }, hideError: false });
Expand Down
27 changes: 23 additions & 4 deletions packages/fluentui-rc/src/GridTemplate/GridTemplate.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
import { Flex } from '@fluentui/react-migration-v0-v9';
import { GridShim, grid } from '@fluentui/react-migration-v0-v9';
import { GridTemplateProps } from '@rjsf/utils';

/** Renders a `GridTemplate` for fluentui-rc, which is expecting the column sizing information coming in via the
* extra props provided by the caller, which are spread directly on the `Flex`.
* `style` by the caller, which are spread directly on the `GridShim` if `columns` or `rows` are provided. Otherwise,
* the `style` is added to a simple grid. This was done because `fluentui-rc` uses the CSS Grid which defines all of
* the column/row/grid information via style.
*
* @param props - The GridTemplateProps, including the extra props containing the fluentui-rc grid positioning details
*/
export default function GridTemplate(props: GridTemplateProps) {
const { children, column, ...rest } = props;
return <Flex {...rest}>{children}</Flex>;
const { children, column, columns, rows, style, ...rest } = props;
if (columns || rows) {
// Use the `grid` rows/columns functions to generate the additional grid styling
const styles = {
...style,
...(rows ? grid.rows(rows) : undefined),
...(columns ? grid.columns(columns) : undefined),
};
return (
<GridShim style={styles} {...rest}>
{children}
</GridShim>
);
}
return (
<div style={style} {...rest}>
{children}
</div>
);
}
3 changes: 2 additions & 1 deletion packages/fluentui-rc/src/RadioWidget/RadioWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSc
onBlur,
onFocus,
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, emptyValue } = options;
const { enumOptions, enumDisabled, emptyValue, inline } = options;

const _onChange = (_: any, data: RadioGroupOnChangeData) =>
onChange(enumOptionsValueForIndex<S>(data.value, enumOptions, emptyValue));
Expand All @@ -52,6 +52,7 @@ export default function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSc
<RadioGroup
id={id}
name={id}
layout={inline ? 'horizontal' : 'vertical'}
value={selectedIndex as string | undefined}
onChange={_onChange}
onBlur={_onBlur}
Expand Down
7 changes: 3 additions & 4 deletions packages/playground/src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Theme as MuiV5Theme } from '@rjsf/mui';
import { Theme as MuiTheme } from '@rjsf/mui';
import { Theme as FluentUIRCTheme } from '@rjsf/fluentui-rc';
import { Theme as SuiTheme } from '@rjsf/semantic-ui';
import { Theme as AntdTheme } from '@rjsf/antd';
Expand Down Expand Up @@ -26,7 +26,6 @@ const validators: PlaygroundProps['validators'] = {
AJV8_es: esV8Validator,
AJV8_2019,
AJV8_2020,
'AJV6 (deprecated)': v6Validator,
};

const themes: PlaygroundProps['themes'] = {
Expand Down Expand Up @@ -102,9 +101,9 @@ const themes: PlaygroundProps['themes'] = {
stylesheet: '',
theme: FluentUIRCTheme,
},
'material-ui-5': {
mui: {
stylesheet: '',
theme: MuiV5Theme,
theme: MuiTheme,
},
'react-bootstrap': {
stylesheet: '//cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css',
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/src/components/DemoFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function DemoFrame(props: DemoFrameProps) {
}, []);

let body: ReactNode = children;
if (theme === 'material-ui-5') {
if (theme === 'mui') {
body = ready ? (
<CacheProvider value={emotionCache}>
<CssBaseline />
Expand Down
15 changes: 9 additions & 6 deletions packages/playground/src/components/Editors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const monacoEditorOptions = {
type EditorProps = {
title: string;
code: string;
onChange: (code: string) => void;
onChange: (data: any) => void;
};

function Editor({ title, code, onChange }: EditorProps) {
Expand Down Expand Up @@ -69,6 +69,7 @@ type EditorsProps = {
extraErrors: ErrorSchema | undefined;
setExtraErrors: React.Dispatch<React.SetStateAction<ErrorSchema | undefined>>;
setShareURL: React.Dispatch<React.SetStateAction<string | null>>;
hasUiSchemaGenerator: boolean;
};

export default function Editors({
Expand All @@ -81,25 +82,26 @@ export default function Editors({
setSchema,
setShareURL,
setUiSchema,
hasUiSchemaGenerator,
}: EditorsProps) {
const onSchemaEdited = useCallback(
(newSchema) => {
(newSchema: any) => {
setSchema(newSchema);
setShareURL(null);
},
[setSchema, setShareURL]
);

const onUISchemaEdited = useCallback(
(newUiSchema) => {
(newUiSchema: any) => {
setUiSchema(newUiSchema);
setShareURL(null);
},
[setUiSchema, setShareURL]
);

const onFormDataEdited = useCallback(
(newFormData) => {
(newFormData: any) => {
if (
!isEqualWith(newFormData, formData, (newValue, oldValue) => {
// Since this is coming from the editor which uses JSON.stringify to trim undefined values compare the values
Expand All @@ -116,19 +118,20 @@ export default function Editors({
);

const onExtraErrorsEdited = useCallback(
(newExtraErrors) => {
(newExtraErrors: any) => {
setExtraErrors(newExtraErrors);
setShareURL(null);
},
[setExtraErrors, setShareURL]
);
const uiSchemaTitle = hasUiSchemaGenerator ? 'UISchema (regenerated on theme change)' : 'UiSchema';

return (
<div className='col-sm-7'>
<Editor title='JSONSchema' code={toJson(schema)} onChange={onSchemaEdited} />
<div className='row'>
<div className='col-sm-6'>
<Editor title='UISchema' code={toJson(uiSchema)} onChange={onUISchemaEdited} />
<Editor title={uiSchemaTitle} code={toJson(uiSchema)} onChange={onUISchemaEdited} />
</div>
<div className='col-sm-6'>
<Editor title='formData' code={toJson(formData)} onChange={onFormDataEdited} />
Expand Down
11 changes: 7 additions & 4 deletions packages/playground/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import base64 from '../utils/base64';

import CopyLink from './CopyLink';
import ThemeSelector, { ThemesType } from './ThemeSelector';
import Selector from './Selector';
import SampleSelector, { SampleSelectorProps } from './SampleSelector';
import ValidatorSelector from './ValidatorSelector';
import SubthemeSelector from './SubthemeSelector';
import RawValidatorTest from './RawValidatorTest';
Expand Down Expand Up @@ -236,13 +236,14 @@ type HeaderProps = {
themes: { [themeName: string]: ThemesType };
theme: string;
subtheme: string | null;
sampleName: string;
validators: {
[validatorName: string]: ValidatorType<any, RJSFSchema, any>;
};
validator: string;
liveSettings: LiveSettings;
playGroundFormRef: React.MutableRefObject<any>;
load: (data: any) => void;
onSampleSelected: SampleSelectorProps['onSelected'];
onThemeSelected: (theme: string, themeObj: ThemesType) => void;
setSubtheme: React.Dispatch<React.SetStateAction<string | null>>;
setStylesheet: React.Dispatch<React.SetStateAction<string | null>>;
Expand All @@ -263,13 +264,14 @@ export default function Header({
validator,
liveSettings,
playGroundFormRef,
load,
onThemeSelected,
setSubtheme,
setStylesheet,
setValidator,
setLiveSettings,
setShareURL,
sampleName,
onSampleSelected,
}: HeaderProps) {
const onSubthemeSelected = useCallback(
(subtheme: any, { stylesheet }: { stylesheet: any }) => {
Expand Down Expand Up @@ -307,6 +309,7 @@ export default function Header({
theme,
liveSettings,
validator,
sampleName,
})
);

Expand All @@ -322,7 +325,7 @@ export default function Header({
<h1>react-jsonschema-form</h1>
<div className='row'>
<div className='col-sm-4'>
<Selector onSelected={load} />
<SampleSelector onSelected={onSampleSelected} selectedSample={sampleName} />
</div>
<div className='col-sm-2'>
<Form
Expand Down
Loading