Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ra-no-code - Introduce Resource Configuration #6217

Merged
merged 14 commits into from
May 6, 2021
2 changes: 1 addition & 1 deletion packages/ra-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
},
"dependencies": {
"classnames": "~2.2.5",
"date-fns": "^1.29.0",
"date-fns": "^2.21.1",
djhi marked this conversation as resolved.
Show resolved Hide resolved
"eventemitter3": "^3.0.0",
"inflection": "~1.12.0",
"lodash": "~4.17.5",
Expand Down
5 changes: 3 additions & 2 deletions packages/ra-core/src/inference/assertions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import parseDate from 'date-fns/parse';
import isValid from 'date-fns/isValid';
import parseDate from 'date-fns/parseISO';

export const isNumeric = (value: any) =>
!isNaN(parseFloat(value)) && isFinite(value);
Expand Down Expand Up @@ -45,7 +46,7 @@ export const isDate = (value: any) => !value || value instanceof Date;
export const valuesAreDate = (values: any[]) => values.every(isDate);

export const isDateString = (value: any) =>
!value || (typeof value === 'string' && !isNaN(parseDate(value).getDate()));
!value || (typeof value === 'string' && isValid(parseDate(value)));
export const valuesAreDateString = (values: any[]) =>
values.every(isDateString);

Expand Down
21 changes: 15 additions & 6 deletions packages/ra-core/src/inference/inferTypeFromValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
valuesAreEmail,
} from './assertions';

const types = [
export const InferenceTypes = [
'array',
'boolean',
'date',
Expand All @@ -33,9 +33,10 @@ const types = [
'richText',
'string',
'url',
'object',
] as const;

export type PossibleInferredElementTypes = typeof types[number];
export type PossibleInferredElementTypes = typeof InferenceTypes[number];

export interface InferredElementDescription {
type: PossibleInferredElementTypes;
Expand Down Expand Up @@ -170,10 +171,18 @@ export const inferTypeFromValues = (
}
if (valuesAreObject(values)) {
// we need to go deeper
// Arbitrarily, choose the first prop of the first object
const propName = Object.keys(values[0]).shift();
const leafValues = values.map(v => v[propName]);
return inferTypeFromValues(`${name}.${propName}`, leafValues);
djhi marked this conversation as resolved.
Show resolved Hide resolved
// Arbitrarily, choose the first object
// FIXME bad visual representation
djhi marked this conversation as resolved.
Show resolved Hide resolved
return {
type: 'object',
props: { source: name },
children: Object.keys(values[0]).map(leafName =>
inferTypeFromValues(
leafName,
values.map(value => value[leafName])
)
),
};
}
return { type: 'string', props: { source: name } };
};
14 changes: 12 additions & 2 deletions packages/ra-core/src/inference/inferTypesFromValues.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,18 @@ describe('inferTypeFromValues', () => {
{ bar: 3, baz: 4 },
])
).toEqual({
type: 'number',
props: { source: 'foo.bar' },
type: 'object',
props: { source: 'foo' },
children: [
{
type: 'number',
props: { source: 'bar' },
},
{
type: 'number',
props: { source: 'baz' },
},
],
});
});
});
2 changes: 1 addition & 1 deletion packages/ra-no-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"dependencies": {
"classnames": "~2.2.5",
"date-fns": "^1.29.0",
"date-fns": "^2.21.1",
"inflection": "~1.12.0",
"lodash": "~4.17.5",
"react-admin": "^3.14.5",
Expand Down
3 changes: 1 addition & 2 deletions packages/ra-no-code/src/Admin.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ describe('Admin', () => {
getByText('Reference', { selector: 'th *' });
getByText('Date', { selector: 'th *' });
getByText('Customer', { selector: 'th *' });
getByText('Basket.product', { selector: 'th *' });
getByText('Basket.quantity', { selector: 'th *' });
getByText('Basket', { selector: 'th *' });
getByText('Total ex taxes', { selector: 'th *' });
getByText('Delivery fees', { selector: 'th *' });
getByText('Tax rate', { selector: 'th *' });
Expand Down
15 changes: 14 additions & 1 deletion packages/ra-no-code/src/Admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,26 @@ import {
Resource,
} from 'react-admin';
import localStorageDataProvider from 'ra-data-local-storage';
import { Create, Edit, List } from './builders';
import { Create, Edit, List, Show } from './builders';
import {
useResourcesConfiguration,
ResourceConfigurationPage,
ResourceConfigurationProvider,
} from './ResourceConfiguration';
import { Layout, Ready } from './ui';
import { Route } from 'react-router';

const dataProvider = localStorageDataProvider();

const customRoutes = [
<Route
path="/configure/:resource"
render={({ match }) => (
<ResourceConfigurationPage resource={match.params.resource} />
)}
/>,
];

export const Admin = (props: AdminProps) => (
<ResourceConfigurationProvider dataProvider={dataProvider}>
<InnerAdmin {...props} />
Expand All @@ -28,6 +39,7 @@ const InnerAdmin = (props: AdminProps) => {
dataProvider={dataProvider}
ready={Ready}
layout={Layout}
customRoutes={customRoutes}
{...props}
>
{hasResources
Expand All @@ -39,6 +51,7 @@ const InnerAdmin = (props: AdminProps) => {
list={List}
edit={Edit}
create={Create}
show={Show}
/>
))
: undefined}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as React from 'react';
import {
getFieldLabelTranslationArgs,
InferenceTypes,
useTranslate,
} from 'ra-core';
import { CheckboxGroupInput, SelectInput, TextInput } from 'ra-ui-materialui';
import { CardContent } from '@material-ui/core';

export const FieldConfigurationFormSection = props => {
const { index, field, resource } = props;
const translate = useTranslate();
const labelArgs = getFieldLabelTranslationArgs({
source: field.props.source,
resource,
label: field.props.label,
});

return (
<CardContent>
<TextInput
source={`fields[${index}].props.source`}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As $index can contain a dot, you should use lodash's get

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this comment

Copy link
Collaborator Author

@djhi djhi May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not because fields is the array of fields in the resource configuration object

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll make it clearer though

label="Source"
fullWidth
disabled
/>
<TextInput
source={`fields[${index}].props.label`}
label="Label"
fullWidth
initialValue={translate(...labelArgs)}
/>
<SelectInput
djhi marked this conversation as resolved.
Show resolved Hide resolved
source={`fields[${index}].type`}
label="Type"
fullWidth
choices={INFERENCE_TYPES}
/>
<CheckboxGroupInput
djhi marked this conversation as resolved.
Show resolved Hide resolved
source={`fields[${index}].views`}
label="Views"
fullWidth
choices={VIEWS}
initialValue={VIEWS_INITIAL_VALUE}
/>
</CardContent>
);
};

const INFERENCE_TYPES = InferenceTypes.map(type => ({
id: type,
name: type,
}));

const VIEWS = [
{
id: 'list',
name: 'List',
},
{
id: 'edit',
name: 'Edit',
},
{
id: 'create',
name: 'Create',
},
{
id: 'show',
name: 'Show',
},
];

const VIEWS_INITIAL_VALUE = ['list', 'edit', 'create', 'show'];
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as React from 'react';
import { Tab } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import { getFieldLabelTranslationArgs, useTranslate } from 'ra-core';

export const FieldConfigurationTab = ({ field, resource, ...props }) => {
const classes = useStyles();
const translate = useTranslate();
const labelArgs = getFieldLabelTranslationArgs({
source: field.props.source,
resource,
label: field.props.label,
});

return (
<Tab
{...props}
key={field.props.source}
label={translate(...labelArgs)}
id={`nav-tab-${field.props.source}`}
aria-controls={`nav-tabpanel-${field.props.source}`}
classes={classes}
/>
);
};

const useStyles = makeStyles(theme => ({
root: {
paddingTop: theme.spacing(1),
paddingBottom: theme.spacing(1),
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
textTransform: 'none',
minHeight: 0,
fontWeight: 'normal',
},
selected: {
fontWeight: 'bold',
},
}));
Loading