Skip to content

Commit

Permalink
Merge branch 'main' into chore/add-jwt-backend-and-stories
Browse files Browse the repository at this point in the history
  • Loading branch information
slax57 committed Apr 5, 2024
2 parents acd11d2 + 5f80c05 commit 23e0760
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 216 deletions.
11 changes: 10 additions & 1 deletion src/AdminGuesser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,13 @@ const AdminGuesser = ({
// Props for AdminResourcesGuesser
includeDeprecated = false,
// Admin props
basename,
store,
dataProvider,
i18nProvider,
authProvider,

Check failure on line 120 in src/AdminGuesser.tsx

View workflow job for this annotation

GitHub Actions / Continuous integration

Duplicate identifier 'authProvider'.
queryClient,
defaultTheme,
layout = Layout,
loginPage = LoginPage,
loading: loadingPage,
Expand Down Expand Up @@ -169,10 +174,14 @@ const AdminGuesser = ({
<AdminContext
i18nProvider={i18nProvider}
dataProvider={dataProvider}
basename={basename}
authProvider={authProvider}
store={store}
queryClient={queryClient}
theme={theme}
darkTheme={darkTheme}
lightTheme={lightTheme}
authProvider={authProvider}>
defaultTheme={defaultTheme}>
<IntrospectionContext.Provider value={introspectionContext}>
<SchemaAnalyzerContext.Provider value={schemaAnalyzer}>
<AdminResourcesGuesser
Expand Down
99 changes: 10 additions & 89 deletions src/CreateGuesser.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import React, { useCallback } from 'react';
import type { PropsWithChildren, ReactNode } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import {
Create,
FileInput,
FormTab,
SimpleForm,
TabbedForm,
useCreate,
useNotify,
useRedirect,
useResourceContext,
} from 'react-admin';
import type { HttpError, RaRecord } from 'react-admin';
import type { Field, Resource } from '@api-platform/api-doc-parser';

import InputGuesser from './InputGuesser.js';
import Introspecter from './Introspecter.js';
import useDisplayOverrideCode from './useDisplayOverrideCode.js';
import useOnSubmit from './useOnSubmit.js';
import type {
CreateGuesserProps,
IntrospectedCreateGuesserProps,
Expand Down Expand Up @@ -63,9 +58,14 @@ export const IntrospectedCreateGuesser = ({
children,
...props
}: IntrospectedCreateGuesserProps) => {
const [create] = useCreate();
const notify = useNotify();
const redirect = useRedirect();
const save = useOnSubmit({
resource,
schemaAnalyzer,
fields,
mutationOptions,
transform,
redirectTo,
});

const displayOverrideCode = useDisplayOverrideCode();

Expand All @@ -77,85 +77,6 @@ export const IntrospectedCreateGuesser = ({
displayOverrideCode(getOverrideCode(schema, writableFields));
}

const hasFileFieldElement = (elements: Array<ReactNode>): boolean =>
elements.some(
(child) =>
React.isValidElement(child) &&
(child.type === FileInput ||
hasFileFieldElement(
React.Children.toArray((child.props as PropsWithChildren).children),
)),
);
const hasFileField = hasFileFieldElement(inputChildren);

const save = useCallback(
async (values: Partial<RaRecord>) => {
let data = values;
if (transform) {
data = transform(values);
}
try {
const response = await create(
resource,
{
data: { ...data, extraInformation: { hasFileField } },
},
{ returnPromise: true },
);
const success =
mutationOptions?.onSuccess ??
((newRecord: RaRecord) => {
notify('ra.notification.created', {
type: 'info',
messageArgs: { smart_count: 1 },
});
redirect(redirectTo, resource, newRecord.id, newRecord);
});
success(response, { data: response }, {});
return undefined;
} catch (mutateError) {
const submissionErrors = schemaAnalyzer.getSubmissionErrors(
mutateError as HttpError,
);
const failure =
mutationOptions?.onError ??
((error: string | Error) => {
let message = 'ra.notification.http_error';
if (!submissionErrors) {
message =
typeof error === 'string' ? error : error.message || message;
}
let errorMessage;
if (typeof error === 'string') {
errorMessage = error;
} else if (error?.message) {
errorMessage = error.message;
}
notify(message, {
type: 'warning',
messageArgs: { _: errorMessage },
});
});
failure(mutateError as string | Error, { data: values }, {});
if (submissionErrors) {
return submissionErrors;
}
return {};
}
},
[
create,
hasFileField,
resource,
mutationOptions,
notify,
redirect,
redirectTo,
schemaAnalyzer,
transform,
],
);

const hasFormTab = inputChildren.some(
(child) =>
typeof child === 'object' && 'type' in child && child.type === FormTab,
Expand Down
127 changes: 10 additions & 117 deletions src/EditGuesser.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import React, { useCallback } from 'react';
import type { PropsWithChildren, ReactNode } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import {
Edit,
FileInput,
FormTab,
SimpleForm,
TabbedForm,
useNotify,
useRedirect,
useResourceContext,
useUpdate,
} from 'react-admin';
import type { HttpError, RaRecord } from 'react-admin';
import { useParams } from 'react-router-dom';
import type { Field, Resource } from '@api-platform/api-doc-parser';

import InputGuesser from './InputGuesser.js';
import Introspecter from './Introspecter.js';
import getIdentifierValue from './getIdentifierValue.js';
import useMercureSubscription from './useMercureSubscription.js';
import useDisplayOverrideCode from './useDisplayOverrideCode.js';
import useOnSubmit from './useOnSubmit.js';
import type {
EditGuesserProps,
IntrospectedEditGuesserProps,
Expand Down Expand Up @@ -69,12 +63,16 @@ export const IntrospectedEditGuesser = ({
}: IntrospectedEditGuesserProps) => {
const { id: routeId } = useParams<'id'>();
const id = decodeURIComponent(routeId ?? '');
const save = useOnSubmit({
resource,
schemaAnalyzer,
fields,
mutationOptions,
transform,
redirectTo,
});
useMercureSubscription(resource, id);

const [update] = useUpdate();
const notify = useNotify();
const redirect = useRedirect();

const displayOverrideCode = useDisplayOverrideCode();

let inputChildren = React.Children.toArray(children);
Expand All @@ -84,107 +82,6 @@ export const IntrospectedEditGuesser = ({
));
displayOverrideCode(getOverrideCode(schema, writableFields));
}
const hasFileFieldElement = (elements: Array<ReactNode>): boolean =>
elements.some(
(child) =>
React.isValidElement(child) &&
(child.type === FileInput ||
hasFileFieldElement(
React.Children.toArray((child.props as PropsWithChildren).children),
)),
);
const hasFileField = hasFileFieldElement(inputChildren);

const save = useCallback(
async (values: Partial<RaRecord>) => {
if (id === undefined) {
return undefined;
}
let data = values;
if (transform) {
data = transform(values);
}
// Identifiers need to be formatted in case they have not been modified in the form.
Object.entries(values).forEach(([key, value]) => {
const identifierValue = getIdentifierValue(
schemaAnalyzer,
resource,
fields,
key,
value,
);
if (identifierValue !== value) {
data[key] = identifierValue;
}
});
try {
const response = await update(
resource,
{
id,
data: { ...data, extraInformation: { hasFileField } },
},
{ returnPromise: true },
);
const success =
mutationOptions?.onSuccess ??
((updatedRecord: RaRecord) => {
notify('ra.notification.updated', {
type: 'info',
messageArgs: { smart_count: 1 },
});
redirect(redirectTo, resource, updatedRecord.id, updatedRecord);
});
success(response, { id, data: response, previousData: values }, {});
return undefined;
} catch (mutateError) {
const submissionErrors = schemaAnalyzer.getSubmissionErrors(
mutateError as HttpError,
);
const failure =
mutationOptions?.onError ??
((error: string | Error) => {
let message = 'ra.notification.http_error';
if (!submissionErrors) {
message =
typeof error === 'string' ? error : error.message || message;
}
let errorMessage;
if (typeof error === 'string') {
errorMessage = error;
} else if (error?.message) {
errorMessage = error.message;
}
notify(message, {
type: 'warning',
messageArgs: { _: errorMessage },
});
});
failure(
mutateError as string | Error,
{ id, data: values, previousData: values },
{},
);
if (submissionErrors) {
return submissionErrors;
}
return {};
}
},
[
fields,
hasFileField,
id,
mutationOptions,
notify,
redirect,
redirectTo,
resource,
schemaAnalyzer,
transform,
update,
],
);

const hasFormTab = inputChildren.some(
(child) =>
Expand All @@ -199,10 +96,6 @@ export const IntrospectedEditGuesser = ({
mutationMode={mutationMode}
redirect={redirectTo}
component={viewComponent}
transform={(data: Partial<RaRecord>) => ({
...data,
extraInformation: { hasFileField },
})}
{...props}>
<FormType
onSubmit={mutationMode !== 'pessimistic' ? undefined : save}
Expand Down
12 changes: 6 additions & 6 deletions src/hydra/dataProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,9 @@ describe('Transform a React Admin request to an Hydra request', () => {
data: {
bar: 'baz',
foo: 'foo',
extraInformation: {
hasFileField: true,
},
},
meta: {
hasFileField: true,
},
});
const url = mockFetchHydra.mock.calls?.[0]?.[0] ?? new URL('https://foo');
Expand Down Expand Up @@ -360,9 +360,9 @@ describe('Transform a React Admin request to an Hydra request', () => {
foo: 'foo',
bar: 'baz',
qux: null,
extraInformation: {
hasFileField: true,
},
},
meta: {
hasFileField: true,
},
previousData: {
id: '/entrypoint/resource/1',
Expand Down
5 changes: 2 additions & 3 deletions src/hydra/dataProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,8 @@ function dataProvider(
}
});
let extraInformation: { hasFileField?: boolean } = {};
if ('data' in params && params.data.extraInformation) {
extraInformation = params.data.extraInformation;
delete params.data.extraInformation;
if ('meta' in params) {
extraInformation = params.meta;
}
const updateHttpMethod = extraInformation.hasFileField ? 'POST' : 'PUT';

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ShowGuesser from './ShowGuesser.js';
import useIntrospect from './useIntrospect.js';
import useIntrospection from './useIntrospection.js';
import useMercureSubscription from './useMercureSubscription.js';
import useOnSubmit from './useOnSubmit.js';

export {
AdminGuesser,
Expand All @@ -26,6 +27,7 @@ export {
useIntrospect,
useIntrospection,
useMercureSubscription,
useOnSubmit,
};
export {
HydraAdmin,
Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -504,3 +504,10 @@ export type IntrospecterProps = (
| ShowGuesserProps
) &
BaseIntrospecterProps;

export type UseOnSubmitProps = Pick<
IntrospectedGuesserProps,
'schemaAnalyzer' | 'resource' | 'fields'
> &
Pick<CreateProps, 'mutationOptions' | 'transform'> &
PickRename<CreateProps, 'redirect', 'redirectTo'>;
Loading

0 comments on commit 23e0760

Please sign in to comment.