Skip to content

Commit

Permalink
ra-no-code Add support for simple references
Browse files Browse the repository at this point in the history
  • Loading branch information
djhi committed May 5, 2021
1 parent 6c243ec commit 6ab0d15
Show file tree
Hide file tree
Showing 11 changed files with 259 additions and 17 deletions.
Original file line number Diff line number Diff line change
@@ -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 (
<>
<SelectInput
source={`${sourcePrefix}.props.reference`}
label="Referenced resource"
fullWidth
choices={Object.keys(resources).map(name => ({
id: name,
name: resources[name].label || resources[name].name,
}))}
/>
<SelectInput
source={`${sourcePrefix}.options.selectionType`}
label="How to select the reference"
fullWidth
choices={ReferenceSelectionChoice}
/>
<FormDataConsumer>
{({ formData, ...rest }) => {
const resourceName = get(
formData,
`${sourcePrefix}.props.reference`
);
if (!resourceName) return null;

const resource = resources[resourceName];
return (
<SelectInput
source={`${sourcePrefix}.options.referenceField`}
label="Displayed field"
choices={resource.fields.map(field => ({
id: field.props.source,
name:
field.props.label ||
field.props.source,
}))}
{...rest}
/>
);
}}
</FormDataConsumer>
</>
);
default:
return null;
}
};

const ReferenceSelectionChoice = [
{ id: 'select', name: 'Simple list' },
{ id: 'autocomplete', name: 'Searchable list' },
{ id: 'radio', name: 'Radio buttons' },
];
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -38,6 +39,10 @@ export const FieldConfigurationFormSection = props => {
label="Views"
fullWidth
/>
<ConfigurationInputsFromFieldDefinition
definition={field}
sourcePrefix={sourcePrefix}
/>
</CardContent>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
};
});
};
10 changes: 8 additions & 2 deletions packages/ra-no-code/src/builders/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
<RaCreate {...props}>
Expand All @@ -17,13 +20,16 @@ export const Create = (props: CreateProps) => (

export const CreateForm = (props: Omit<SimpleFormProps, 'children'>) => {
const resource = useResourceContext(props);
const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);

return (
<SimpleForm {...props}>
{resourceConfiguration.fields
.filter(definition => definition.views.includes('create'))
.map(definition => getInputFromFieldDefinition(definition))}
.map(definition =>
getInputFromFieldDefinition(definition, resources)
)}
</SimpleForm>
);
};
10 changes: 8 additions & 2 deletions packages/ra-no-code/src/builders/Edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
Expand All @@ -17,13 +20,16 @@ export const Edit = (props: EditProps) => (

export const EditForm = (props: Omit<SimpleFormProps, 'children'>) => {
const resource = useResourceContext(props);
const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);

return (
<SimpleForm {...props}>
{resourceConfiguration.fields
.filter(definition => definition.views.includes('edit'))
.map(definition => getInputFromFieldDefinition(definition))}
.map(definition =>
getInputFromFieldDefinition(definition, resources)
)}
</SimpleForm>
);
};
10 changes: 8 additions & 2 deletions packages/ra-no-code/src/builders/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
Expand All @@ -18,13 +21,16 @@ export const List = (props: ListProps) => (

export const Datagrid = (props: Omit<DatagridProps, 'children'>) => {
const resource = useResourceContext(props);
const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);

return (
<RaDatagrid rowClick="edit" {...props}>
{resourceConfiguration.fields
.filter(definition => definition.views.includes('list'))
.map(definition => getFieldFromFieldDefinition(definition))}
.map(definition =>
getFieldFromFieldDefinition(definition, resources)
)}
</RaDatagrid>
);
};
Original file line number Diff line number Diff line change
@@ -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 (
<SelectInput
optionText={definition.options.referenceField}
{...props}
/>
);
}

if (definition.options.selectionType === 'autocomplete') {
return (
<AutocompleteInput
optionText={definition.options.referenceField}
{...props}
/>
);
}

if (definition.options.selectionType === 'radio') {
return (
<RadioButtonGroupInput
optionText={definition.options.referenceField}
{...props}
/>
);
}
};

interface ReferenceInputChildFromDefinitionProps {
definition: ReferenceFieldConfiguration;
}
10 changes: 8 additions & 2 deletions packages/ra-no-code/src/builders/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
Expand All @@ -17,13 +20,16 @@ export const Show = (props: ShowProps) => (

export const ShowForm = (props: Omit<SimpleShowLayoutProps, 'children'>) => {
const resource = useResourceContext(props);
const [resources] = useResourcesConfiguration();
const [resourceConfiguration] = useResourceConfiguration(resource);

return (
<SimpleShowLayout {...props}>
{resourceConfiguration.fields
.filter(definition => definition.views.includes('show'))
.map(definition => getFieldFromFieldDefinition(definition))}
.map(definition =>
getFieldFromFieldDefinition(definition, resources)
)}
</SimpleShowLayout>
);
};
32 changes: 31 additions & 1 deletion packages/ra-no-code/src/builders/getFieldFromFieldDefinition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down Expand Up @@ -53,6 +59,30 @@ export const getFieldFromFieldDefinition = (
return (
<UrlField key={definition.props.source} {...definition.props} />
);
case 'reference':
const reference = resources[definition.props.reference];

if (reference) {
const field = reference.fields.find(
field =>
field.props.source === definition.options.referenceField
);
return (
<ReferenceField
key={definition.props.source}
{...definition.props}
>
{getFieldFromFieldDefinition(field, resources)}
</ReferenceField>
);
}

return (
<TextField
key={definition.props.source}
{...definition.props}
/>
);
default:
return (
<TextField
Expand Down
Loading

0 comments on commit 6ab0d15

Please sign in to comment.