diff --git a/examples/no-code/src/main.tsx b/examples/no-code/src/main.tsx
index 72e4f0ceed0..77c0ef213ef 100644
--- a/examples/no-code/src/main.tsx
+++ b/examples/no-code/src/main.tsx
@@ -1,10 +1,21 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Root } from 'ra-no-code';
+import { defaultTheme } from 'react-admin';
+import {
+ unstable_createMuiStrictModeTheme,
+ createMuiTheme,
+} from '@material-ui/core/styles';
+
+// FIXME MUI bug https://github.com/mui-org/material-ui/issues/13394
+const theme =
+ process.env.NODE_ENV !== 'production'
+ ? unstable_createMuiStrictModeTheme(defaultTheme)
+ : createMuiTheme(defaultTheme);
ReactDOM.render(
-
+
,
document.getElementById('root')
);
diff --git a/packages/ra-no-code/src/ApplicationsDashboard/ApplicationsDashboard.tsx b/packages/ra-no-code/src/ApplicationsDashboard/ApplicationsDashboard.tsx
index 0bd8bcd0574..0422b62d5ce 100644
--- a/packages/ra-no-code/src/ApplicationsDashboard/ApplicationsDashboard.tsx
+++ b/packages/ra-no-code/src/ApplicationsDashboard/ApplicationsDashboard.tsx
@@ -14,8 +14,12 @@ import {
createMuiTheme,
makeStyles,
ThemeProvider,
+ unstable_createMuiStrictModeTheme,
} from '@material-ui/core/styles';
-import { defaultTheme } from 'ra-ui-materialui';
+import {
+ defaultTheme as RaDefaultTheme,
+ RaThemeOptions,
+} from 'ra-ui-materialui';
import FolderIcon from '@material-ui/icons/Folder';
import { Application } from './types';
import { NewApplicationForm } from './NewApplicationForm';
@@ -24,8 +28,19 @@ import {
storeApplicationsInStorage,
} from './applicationStorage';
-export const ApplicationsDashboard = ({ onApplicationSelected }) => (
-
+const defaultTheme =
+ process.env.NODE_ENV !== 'production'
+ ? unstable_createMuiStrictModeTheme(RaDefaultTheme)
+ : createMuiTheme(RaDefaultTheme);
+
+export const ApplicationsDashboard = ({
+ onApplicationSelected,
+ theme = defaultTheme,
+}: {
+ onApplicationSelected: any;
+ theme: RaThemeOptions;
+}) => (
+
);
diff --git a/packages/ra-no-code/src/ResourceConfiguration/ConfigurationInputsFromFieldDefinition.tsx b/packages/ra-no-code/src/ResourceConfiguration/ConfigurationInputsFromFieldDefinition.tsx
new file mode 100644
index 00000000000..549ec698ed7
--- /dev/null
+++ b/packages/ra-no-code/src/ResourceConfiguration/ConfigurationInputsFromFieldDefinition.tsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import { FormDataConsumer, InferredElementDescription } from 'ra-core';
+import { SelectInput } from 'ra-ui-materialui';
+import get from 'lodash/get';
+import { useResourcesConfiguration } from './useResourcesConfiguration';
+
+export const ConfigurationInputsFromFieldDefinition = ({
+ definition,
+ sourcePrefix,
+}: {
+ definition: InferredElementDescription;
+ sourcePrefix?: string;
+}) => {
+ const [resources] = useResourcesConfiguration();
+
+ switch (definition.type) {
+ case 'reference':
+ return (
+ <>
+ ({
+ id: name,
+ name: resources[name].label || resources[name].name,
+ }))}
+ />
+
+
+ {({ formData, ...rest }) => {
+ const resourceName = get(
+ formData,
+ `${sourcePrefix}.props.reference`
+ );
+ if (!resourceName) return null;
+
+ const resource = resources[resourceName];
+ return (
+ ({
+ id: field.props.source,
+ name:
+ field.props.label ||
+ field.props.source,
+ }))}
+ {...rest}
+ />
+ );
+ }}
+
+ >
+ );
+ default:
+ return null;
+ }
+};
+
+const ReferenceSelectionChoice = [
+ { id: 'select', name: 'Simple list' },
+ { id: 'autocomplete', name: 'Searchable list' },
+ { id: 'radio', name: 'Radio buttons' },
+];
diff --git a/packages/ra-no-code/src/ResourceConfiguration/FieldConfigurationFormSection.tsx b/packages/ra-no-code/src/ResourceConfiguration/FieldConfigurationFormSection.tsx
index d23e25299f6..828b9916bbf 100644
--- a/packages/ra-no-code/src/ResourceConfiguration/FieldConfigurationFormSection.tsx
+++ b/packages/ra-no-code/src/ResourceConfiguration/FieldConfigurationFormSection.tsx
@@ -4,6 +4,7 @@ import { TextInput } from 'ra-ui-materialui';
import { CardContent } from '@material-ui/core';
import { FieldTypeInput } from './FieldConfiguration/FieldTypeInput';
import { FieldViewsInput } from './FieldConfiguration/FieldViewsInput';
+import { ConfigurationInputsFromFieldDefinition } from './ConfigurationInputsFromFieldDefinition';
export const FieldConfigurationFormSection = props => {
const { sourcePrefix, field, resource } = props;
@@ -38,6 +39,10 @@ export const FieldConfigurationFormSection = props => {
label="Views"
fullWidth
/>
+
);
};
diff --git a/packages/ra-no-code/src/ResourceConfiguration/ResourceConfigurationContext.ts b/packages/ra-no-code/src/ResourceConfiguration/ResourceConfigurationContext.ts
index 726430ba0f7..aeb1a0d798c 100644
--- a/packages/ra-no-code/src/ResourceConfiguration/ResourceConfigurationContext.ts
+++ b/packages/ra-no-code/src/ResourceConfiguration/ResourceConfigurationContext.ts
@@ -32,10 +32,25 @@ export type ResourceConfiguration = {
fields?: FieldConfiguration[];
};
-export interface FieldConfiguration extends InferredElementDescription {
+export interface ReferenceFieldConfiguration extends BaseFieldConfiguration {
+ type: 'reference';
+ options: {
+ selectionType: 'select' | 'autocomplete' | 'radio';
+ referenceField: 'string';
+ };
+}
+
+export interface BaseFieldConfiguration extends InferredElementDescription {
views: FieldView[];
+ options?: {
+ [key: string]: any;
+ };
}
+export type FieldConfiguration =
+ | BaseFieldConfiguration
+ | ReferenceFieldConfiguration;
+
export type FieldView = 'list' | 'create' | 'edit' | 'show';
export type ResourceConfigurationMap =
diff --git a/packages/ra-no-code/src/ResourceConfiguration/getFieldDefinitionsFromRecords.ts b/packages/ra-no-code/src/ResourceConfiguration/getFieldDefinitionsFromRecords.ts
index 99517c76699..1242bde30e4 100644
--- a/packages/ra-no-code/src/ResourceConfiguration/getFieldDefinitionsFromRecords.ts
+++ b/packages/ra-no-code/src/ResourceConfiguration/getFieldDefinitionsFromRecords.ts
@@ -6,8 +6,19 @@ export const getFieldDefinitionsFromRecords = (
): FieldConfiguration[] => {
const values = getValuesFromRecords(records);
- return Object.keys(values).map(key => ({
- ...inferTypeFromValues(key, values[key]),
- views: ['list', 'create', 'edit', 'show'],
- }));
+ return Object.keys(values).map(key => {
+ const inferedDefinition = inferTypeFromValues(key, values[key]);
+
+ return {
+ ...inferedDefinition,
+ options:
+ inferedDefinition.type === 'reference'
+ ? {
+ referenceField: 'id',
+ selectionType: 'select',
+ }
+ : undefined,
+ views: ['list', 'create', 'edit', 'show'],
+ };
+ });
};
diff --git a/packages/ra-no-code/src/Root.tsx b/packages/ra-no-code/src/Root.tsx
index df464f18428..ec7d42a924c 100644
--- a/packages/ra-no-code/src/Root.tsx
+++ b/packages/ra-no-code/src/Root.tsx
@@ -1,10 +1,11 @@
+import { RaThemeOptions } from 'ra-ui-materialui';
import * as React from 'react';
import { useMemo, useState } from 'react';
import { Admin } from './Admin';
import { ApplicationContext } from './ApplicationContext';
import { ApplicationsDashboard } from './ApplicationsDashboard';
-export const Root = () => {
+export const Root = ({ theme }: { theme: RaThemeOptions }) => {
const [application, setApplication] = useState();
const handleExitApplication = () => {
@@ -26,7 +27,7 @@ export const Root = () => {
if (context.application) {
return (
-
+
);
}
@@ -34,6 +35,7 @@ export const Root = () => {
return (
);
};
diff --git a/packages/ra-no-code/src/builders/Create.tsx b/packages/ra-no-code/src/builders/Create.tsx
index dd87f57dd84..ebbd0fcd27c 100644
--- a/packages/ra-no-code/src/builders/Create.tsx
+++ b/packages/ra-no-code/src/builders/Create.tsx
@@ -7,7 +7,10 @@ import {
SimpleFormProps,
} from 'ra-ui-materialui';
import { getInputFromFieldDefinition } from './getInputFromFieldDefinition';
-import { useResourceConfiguration } from '../ResourceConfiguration';
+import {
+ useResourceConfiguration,
+ useResourcesConfiguration,
+} from '../ResourceConfiguration';
export const Create = (props: CreateProps) => (
@@ -17,13 +20,16 @@ export const Create = (props: CreateProps) => (
export const CreateForm = (props: Omit) => {
const resource = useResourceContext(props);
+ const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);
return (
{resourceConfiguration.fields
.filter(definition => definition.views.includes('create'))
- .map(definition => getInputFromFieldDefinition(definition))}
+ .map(definition =>
+ getInputFromFieldDefinition(definition, resources)
+ )}
);
};
diff --git a/packages/ra-no-code/src/builders/Edit.tsx b/packages/ra-no-code/src/builders/Edit.tsx
index b3550c3ae65..5dbae398f94 100644
--- a/packages/ra-no-code/src/builders/Edit.tsx
+++ b/packages/ra-no-code/src/builders/Edit.tsx
@@ -6,7 +6,10 @@ import {
SimpleForm,
SimpleFormProps,
} from 'ra-ui-materialui';
-import { useResourceConfiguration } from '../ResourceConfiguration';
+import {
+ useResourceConfiguration,
+ useResourcesConfiguration,
+} from '../ResourceConfiguration';
import { getInputFromFieldDefinition } from './getInputFromFieldDefinition';
export const Edit = (props: EditProps) => (
@@ -17,13 +20,16 @@ export const Edit = (props: EditProps) => (
export const EditForm = (props: Omit) => {
const resource = useResourceContext(props);
+ const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);
return (
{resourceConfiguration.fields
.filter(definition => definition.views.includes('edit'))
- .map(definition => getInputFromFieldDefinition(definition))}
+ .map(definition =>
+ getInputFromFieldDefinition(definition, resources)
+ )}
);
};
diff --git a/packages/ra-no-code/src/builders/List.tsx b/packages/ra-no-code/src/builders/List.tsx
index 9d066c49d82..2356c07dde6 100644
--- a/packages/ra-no-code/src/builders/List.tsx
+++ b/packages/ra-no-code/src/builders/List.tsx
@@ -7,7 +7,10 @@ import {
ListProps,
} from 'ra-ui-materialui';
-import { useResourceConfiguration } from '../ResourceConfiguration';
+import {
+ useResourceConfiguration,
+ useResourcesConfiguration,
+} from '../ResourceConfiguration';
import { getFieldFromFieldDefinition } from './getFieldFromFieldDefinition';
export const List = (props: ListProps) => (
@@ -18,13 +21,16 @@ export const List = (props: ListProps) => (
export const Datagrid = (props: Omit) => {
const resource = useResourceContext(props);
+ const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);
return (
{resourceConfiguration.fields
.filter(definition => definition.views.includes('list'))
- .map(definition => getFieldFromFieldDefinition(definition))}
+ .map(definition =>
+ getFieldFromFieldDefinition(definition, resources)
+ )}
);
};
diff --git a/packages/ra-no-code/src/builders/ReferenceInputChildFromDefinition.tsx b/packages/ra-no-code/src/builders/ReferenceInputChildFromDefinition.tsx
new file mode 100644
index 00000000000..9b743ec6918
--- /dev/null
+++ b/packages/ra-no-code/src/builders/ReferenceInputChildFromDefinition.tsx
@@ -0,0 +1,43 @@
+import * as React from 'react';
+import {
+ AutocompleteInput,
+ RadioButtonGroupInput,
+ SelectInput,
+} from 'ra-ui-materialui';
+import { ReferenceFieldConfiguration } from '../ResourceConfiguration';
+
+export const ReferenceInputChildFromDefinition = ({
+ definition,
+ ...props
+}: ReferenceInputChildFromDefinitionProps) => {
+ if (definition.options.selectionType === 'select') {
+ return (
+
+ );
+ }
+
+ if (definition.options.selectionType === 'autocomplete') {
+ return (
+
+ );
+ }
+
+ if (definition.options.selectionType === 'radio') {
+ return (
+
+ );
+ }
+};
+
+interface ReferenceInputChildFromDefinitionProps {
+ definition: ReferenceFieldConfiguration;
+}
diff --git a/packages/ra-no-code/src/builders/Show.tsx b/packages/ra-no-code/src/builders/Show.tsx
index 4f07246d063..217805ed504 100644
--- a/packages/ra-no-code/src/builders/Show.tsx
+++ b/packages/ra-no-code/src/builders/Show.tsx
@@ -6,7 +6,10 @@ import {
SimpleShowLayout,
SimpleShowLayoutProps,
} from 'ra-ui-materialui';
-import { useResourceConfiguration } from '../ResourceConfiguration';
+import {
+ useResourceConfiguration,
+ useResourcesConfiguration,
+} from '../ResourceConfiguration';
import { getFieldFromFieldDefinition } from './getFieldFromFieldDefinition';
export const Show = (props: ShowProps) => (
@@ -17,13 +20,16 @@ export const Show = (props: ShowProps) => (
export const ShowForm = (props: Omit) => {
const resource = useResourceContext(props);
+ const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);
return (
{resourceConfiguration.fields
.filter(definition => definition.views.includes('show'))
- .map(definition => getFieldFromFieldDefinition(definition))}
+ .map(definition =>
+ getFieldFromFieldDefinition(definition, resources)
+ )}
);
};
diff --git a/packages/ra-no-code/src/builders/getFieldFromFieldDefinition.tsx b/packages/ra-no-code/src/builders/getFieldFromFieldDefinition.tsx
index c7626c4a5dd..9eb7852e003 100644
--- a/packages/ra-no-code/src/builders/getFieldFromFieldDefinition.tsx
+++ b/packages/ra-no-code/src/builders/getFieldFromFieldDefinition.tsx
@@ -6,12 +6,18 @@ import {
EmailField,
ImageField,
NumberField,
+ ReferenceField,
TextField,
UrlField,
} from 'ra-ui-materialui';
+import {
+ FieldConfiguration,
+ ResourceConfigurationMap,
+} from '../ResourceConfiguration';
export const getFieldFromFieldDefinition = (
- definition: InferredElementDescription
+ definition: FieldConfiguration,
+ resources: ResourceConfigurationMap
) => {
switch (definition.type) {
case 'date':
@@ -53,6 +59,30 @@ export const getFieldFromFieldDefinition = (
return (
);
+ case 'reference':
+ const reference = resources[definition.props.reference];
+
+ if (reference) {
+ const field = reference.fields.find(
+ field =>
+ field.props.source === definition.options.referenceField
+ );
+ return (
+
+ {getFieldFromFieldDefinition(field, resources)}
+
+ );
+ }
+
+ return (
+
+ );
default:
return (
{
switch (definition.type) {
@@ -59,10 +67,46 @@ export const getInputFromFieldDefinition = (
case 'object':
if (Array.isArray(definition.children)) {
return definition.children.map((child, index) =>
- getInputFromFieldDefinition(child, index.toString())
+ getInputFromFieldDefinition(
+ child,
+ resources,
+ index.toString()
+ )
);
}
- return <>{getInputFromFieldDefinition(definition.children)}>;
+ return (
+ <>
+ {getInputFromFieldDefinition(
+ definition.children,
+ resources,
+ undefined
+ )}
+ >
+ );
+ case 'reference':
+ const referenceDefinition = definition as ReferenceFieldConfiguration;
+ const reference = resources[definition.props.reference];
+
+ if (reference) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+
default:
return (