Add rich text editor to category pages
This commit is contained in:
parent
28cc74c954
commit
9adde24a48
5 changed files with 391 additions and 268 deletions
|
@ -2,43 +2,23 @@ import AppHeader from "@saleor/components/AppHeader";
|
||||||
import { CardSpacer } from "@saleor/components/CardSpacer";
|
import { CardSpacer } from "@saleor/components/CardSpacer";
|
||||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import Form from "@saleor/components/Form";
|
import Metadata from "@saleor/components/Metadata";
|
||||||
import Metadata, { MetadataFormData } from "@saleor/components/Metadata";
|
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
import SeoForm from "@saleor/components/SeoForm";
|
import SeoForm from "@saleor/components/SeoForm";
|
||||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
|
||||||
import { ContentState, convertToRaw, RawDraftContentState } from "draft-js";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
|
import CategoryDetailsForm from "../../components/CategoryDetailsForm";
|
||||||
|
import CategoryCreateForm, { CategoryCreateData } from "./form";
|
||||||
export interface FormData extends MetadataFormData {
|
|
||||||
description: RawDraftContentState;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
seoTitle: string;
|
|
||||||
seoDescription: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialData: FormData = {
|
|
||||||
description: convertToRaw(ContentState.createFromText("")),
|
|
||||||
metadata: [],
|
|
||||||
name: "",
|
|
||||||
privateMetadata: [],
|
|
||||||
seoDescription: "",
|
|
||||||
seoTitle: "",
|
|
||||||
slug: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface CategoryCreatePageProps {
|
export interface CategoryCreatePageProps {
|
||||||
errors: ProductErrorFragment[];
|
errors: ProductErrorFragment[];
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
onSubmit(data: FormData);
|
onSubmit(data: CategoryCreateData);
|
||||||
onBack();
|
onBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,16 +30,10 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
||||||
saveButtonBarState
|
saveButtonBarState
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {
|
|
||||||
makeChangeHandler: makeMetadataChangeHandler
|
|
||||||
} = useMetadataChangeTrigger();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form onSubmit={onSubmit} initial={initialData} confirmLeave>
|
|
||||||
{({ data, change, submit, hasChanged }) => {
|
|
||||||
const changeMetadata = makeMetadataChangeHandler(change);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<CategoryCreateForm onSubmit={onSubmit}>
|
||||||
|
{({ data, change, handlers, submit, hasChanged }) => (
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>
|
<AppHeader onBack={onBack}>
|
||||||
{intl.formatMessage(sectionNames.categories)}
|
{intl.formatMessage(sectionNames.categories)}
|
||||||
|
@ -72,10 +46,11 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<CategoryDetailsForm
|
<CategoryDetailsForm
|
||||||
disabled={disabled}
|
|
||||||
data={data}
|
data={data}
|
||||||
onChange={change}
|
disabled={disabled}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
|
onChange={change}
|
||||||
|
onDescriptionChange={handlers.changeDescription}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<SeoForm
|
<SeoForm
|
||||||
|
@ -95,7 +70,7 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<Metadata data={data} onChange={changeMetadata} />
|
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||||
<SaveButtonBar
|
<SaveButtonBar
|
||||||
onCancel={onBack}
|
onCancel={onBack}
|
||||||
onSave={submit}
|
onSave={submit}
|
||||||
|
@ -104,9 +79,8 @@ export const CategoryCreatePage: React.FC<CategoryCreatePageProps> = ({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
)}
|
||||||
}}
|
</CategoryCreateForm>
|
||||||
</Form>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
CategoryCreatePage.displayName = "CategoryCreatePage";
|
CategoryCreatePage.displayName = "CategoryCreatePage";
|
||||||
|
|
100
src/categories/components/CategoryCreatePage/form.tsx
Normal file
100
src/categories/components/CategoryCreatePage/form.tsx
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import { OutputData } from "@editorjs/editorjs";
|
||||||
|
import { MetadataFormData } from "@saleor/components/Metadata";
|
||||||
|
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
|
||||||
|
import useForm, { FormChange } from "@saleor/hooks/useForm";
|
||||||
|
import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit";
|
||||||
|
import getMetadata from "@saleor/utils/metadata/getMetadata";
|
||||||
|
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||||
|
import useRichText from "@saleor/utils/richText/useRichText";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface CategoryCreateFormData extends MetadataFormData {
|
||||||
|
name: string;
|
||||||
|
seoDescription: string;
|
||||||
|
seoTitle: string;
|
||||||
|
slug: string;
|
||||||
|
}
|
||||||
|
export interface CategoryCreateData extends CategoryCreateFormData {
|
||||||
|
description: OutputData;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CategoryCreateHandlers {
|
||||||
|
changeMetadata: FormChange;
|
||||||
|
changeDescription: RichTextEditorChange;
|
||||||
|
}
|
||||||
|
export interface UseCategoryCreateFormResult {
|
||||||
|
change: FormChange;
|
||||||
|
data: CategoryCreateData;
|
||||||
|
handlers: CategoryCreateHandlers;
|
||||||
|
hasChanged: boolean;
|
||||||
|
submit: () => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CategoryCreateFormProps {
|
||||||
|
children: (props: UseCategoryCreateFormResult) => React.ReactNode;
|
||||||
|
onSubmit: (data: CategoryCreateData) => Promise<any[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useCategoryCreateForm(
|
||||||
|
onSubmit: (data: CategoryCreateData) => Promise<any[]>
|
||||||
|
): UseCategoryCreateFormResult {
|
||||||
|
const [changed, setChanged] = React.useState(false);
|
||||||
|
const triggerChange = () => setChanged(true);
|
||||||
|
|
||||||
|
const form = useForm<CategoryCreateFormData>({
|
||||||
|
metadata: [],
|
||||||
|
name: "",
|
||||||
|
privateMetadata: [],
|
||||||
|
seoDescription: "",
|
||||||
|
seoTitle: "",
|
||||||
|
slug: ""
|
||||||
|
});
|
||||||
|
const [description, changeDescription] = useRichText({
|
||||||
|
initial: null,
|
||||||
|
triggerChange
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
isMetadataModified,
|
||||||
|
isPrivateMetadataModified,
|
||||||
|
makeChangeHandler: makeMetadataChangeHandler
|
||||||
|
} = useMetadataChangeTrigger();
|
||||||
|
|
||||||
|
const handleChange: FormChange = (event, cb) => {
|
||||||
|
form.change(event, cb);
|
||||||
|
triggerChange();
|
||||||
|
};
|
||||||
|
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||||
|
|
||||||
|
// Need to make it function to always have description.current up to date
|
||||||
|
const getData = (): CategoryCreateData => ({
|
||||||
|
...form.data,
|
||||||
|
...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified),
|
||||||
|
description: description.current
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => handleFormSubmit(getData(), onSubmit, setChanged);
|
||||||
|
|
||||||
|
return {
|
||||||
|
change: handleChange,
|
||||||
|
data: getData(),
|
||||||
|
handlers: {
|
||||||
|
changeDescription,
|
||||||
|
changeMetadata
|
||||||
|
},
|
||||||
|
hasChanged: changed,
|
||||||
|
submit
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CategoryCreateForm: React.FC<CategoryCreateFormProps> = ({
|
||||||
|
children,
|
||||||
|
onSubmit
|
||||||
|
}) => {
|
||||||
|
const props = useCategoryCreateForm(onSubmit);
|
||||||
|
|
||||||
|
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||||
|
};
|
||||||
|
|
||||||
|
CategoryCreateForm.displayName = "CategoryCreateForm";
|
||||||
|
export default CategoryCreateForm;
|
|
@ -1,35 +1,34 @@
|
||||||
|
import { OutputData } from "@editorjs/editorjs";
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@material-ui/core/CardContent";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import FormSpacer from "@saleor/components/FormSpacer";
|
import FormSpacer from "@saleor/components/FormSpacer";
|
||||||
import RichTextEditor from "@saleor/components/RichTextEditor";
|
import RichTextEditor, {
|
||||||
|
RichTextEditorChange
|
||||||
|
} from "@saleor/components/RichTextEditor";
|
||||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||||
import { commonMessages } from "@saleor/intl";
|
import { commonMessages } from "@saleor/intl";
|
||||||
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
|
import { getFormErrors, getProductErrorMessage } from "@saleor/utils/errors";
|
||||||
import { RawDraftContentState } from "draft-js";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
|
|
||||||
import { maybe } from "../../../misc";
|
|
||||||
import { CategoryDetails_category } from "../../types/CategoryDetails";
|
|
||||||
|
|
||||||
interface CategoryDetailsFormProps {
|
interface CategoryDetailsFormProps {
|
||||||
category?: CategoryDetails_category;
|
|
||||||
data: {
|
data: {
|
||||||
name: string;
|
name: string;
|
||||||
description: RawDraftContentState;
|
description: OutputData;
|
||||||
};
|
};
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
errors: ProductErrorFragment[];
|
errors: ProductErrorFragment[];
|
||||||
onChange: (event: React.ChangeEvent<any>) => void;
|
onChange: (event: React.ChangeEvent<any>) => void;
|
||||||
|
onDescriptionChange: RichTextEditorChange;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({
|
export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({
|
||||||
category,
|
|
||||||
disabled,
|
disabled,
|
||||||
data,
|
data,
|
||||||
onChange,
|
onChange,
|
||||||
|
onDescriptionChange,
|
||||||
errors
|
errors
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
@ -58,15 +57,15 @@ export const CategoryDetailsForm: React.FC<CategoryDetailsFormProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<FormSpacer />
|
<FormSpacer />
|
||||||
<RichTextEditor
|
<RichTextEditor
|
||||||
|
data={data.description}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
error={!!formErrors.descriptionJson}
|
error={!!formErrors.descriptionJson}
|
||||||
helperText={getProductErrorMessage(formErrors.descriptionJson, intl)}
|
helperText={getProductErrorMessage(formErrors.descriptionJson, intl)}
|
||||||
label={intl.formatMessage({
|
label={intl.formatMessage({
|
||||||
defaultMessage: "Category Description"
|
defaultMessage: "Category Description"
|
||||||
})}
|
})}
|
||||||
initial={maybe(() => JSON.parse(category.descriptionJson))}
|
|
||||||
name="description"
|
name="description"
|
||||||
onChange={onChange}
|
onChange={onDescriptionChange}
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
@ -5,9 +5,7 @@ import { CardSpacer } from "@saleor/components/CardSpacer";
|
||||||
import CardTitle from "@saleor/components/CardTitle";
|
import CardTitle from "@saleor/components/CardTitle";
|
||||||
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton";
|
||||||
import Container from "@saleor/components/Container";
|
import Container from "@saleor/components/Container";
|
||||||
import Form from "@saleor/components/Form";
|
|
||||||
import Metadata from "@saleor/components/Metadata/Metadata";
|
import Metadata from "@saleor/components/Metadata/Metadata";
|
||||||
import { MetadataFormData } from "@saleor/components/Metadata/types";
|
|
||||||
import PageHeader from "@saleor/components/PageHeader";
|
import PageHeader from "@saleor/components/PageHeader";
|
||||||
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
import SaveButtonBar from "@saleor/components/SaveButtonBar";
|
||||||
import SeoForm from "@saleor/components/SeoForm";
|
import SeoForm from "@saleor/components/SeoForm";
|
||||||
|
@ -15,9 +13,6 @@ import { Tab, TabContainer } from "@saleor/components/Tab";
|
||||||
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
import { ProductErrorFragment } from "@saleor/fragments/types/ProductErrorFragment";
|
||||||
import { SubmitPromise } from "@saleor/hooks/useForm";
|
import { SubmitPromise } from "@saleor/hooks/useForm";
|
||||||
import { sectionNames } from "@saleor/intl";
|
import { sectionNames } from "@saleor/intl";
|
||||||
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
|
||||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
|
||||||
import { RawDraftContentState } from "draft-js";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FormattedMessage, useIntl } from "react-intl";
|
import { FormattedMessage, useIntl } from "react-intl";
|
||||||
|
|
||||||
|
@ -32,15 +27,7 @@ import {
|
||||||
} from "../../types/CategoryDetails";
|
} from "../../types/CategoryDetails";
|
||||||
import CategoryBackground from "../CategoryBackground";
|
import CategoryBackground from "../CategoryBackground";
|
||||||
import CategoryProducts from "../CategoryProducts";
|
import CategoryProducts from "../CategoryProducts";
|
||||||
|
import CategoryUpdateForm, { CategoryUpdateData } from "./form";
|
||||||
export interface FormData extends MetadataFormData {
|
|
||||||
backgroundImageAlt: string;
|
|
||||||
description: RawDraftContentState;
|
|
||||||
name: string;
|
|
||||||
slug: string;
|
|
||||||
seoTitle: string;
|
|
||||||
seoDescription: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CategoryPageTab {
|
export enum CategoryPageTab {
|
||||||
categories = "categories",
|
categories = "categories",
|
||||||
|
@ -62,7 +49,7 @@ export interface CategoryUpdatePageProps
|
||||||
};
|
};
|
||||||
saveButtonBarState: ConfirmButtonTransitionState;
|
saveButtonBarState: ConfirmButtonTransitionState;
|
||||||
onImageDelete: () => void;
|
onImageDelete: () => void;
|
||||||
onSubmit: (data: FormData) => SubmitPromise;
|
onSubmit: (data: CategoryUpdateData) => SubmitPromise;
|
||||||
onImageUpload(file: File);
|
onImageUpload(file: File);
|
||||||
onNextPage();
|
onNextPage();
|
||||||
onPreviousPage();
|
onPreviousPage();
|
||||||
|
@ -106,64 +93,21 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
toggleAll
|
toggleAll
|
||||||
}: CategoryUpdatePageProps) => {
|
}: CategoryUpdatePageProps) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const {
|
|
||||||
isMetadataModified,
|
|
||||||
isPrivateMetadataModified,
|
|
||||||
makeChangeHandler: makeMetadataChangeHandler
|
|
||||||
} = useMetadataChangeTrigger();
|
|
||||||
|
|
||||||
const initialData: FormData = category
|
|
||||||
? {
|
|
||||||
backgroundImageAlt: maybe(() => category.backgroundImage.alt, ""),
|
|
||||||
description: maybe(() => JSON.parse(category.descriptionJson)),
|
|
||||||
metadata: category?.metadata?.map(mapMetadataItemToInput),
|
|
||||||
name: category.name || "",
|
|
||||||
privateMetadata: category?.privateMetadata?.map(mapMetadataItemToInput),
|
|
||||||
seoDescription: category.seoDescription || "",
|
|
||||||
seoTitle: category.seoTitle || "",
|
|
||||||
slug: category?.slug || ""
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
backgroundImageAlt: "",
|
|
||||||
description: "",
|
|
||||||
metadata: undefined,
|
|
||||||
name: "",
|
|
||||||
privateMetadata: undefined,
|
|
||||||
seoDescription: "",
|
|
||||||
seoTitle: "",
|
|
||||||
slug: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = (data: FormData) => {
|
|
||||||
const metadata = isMetadataModified ? data.metadata : undefined;
|
|
||||||
const privateMetadata = isPrivateMetadataModified
|
|
||||||
? data.privateMetadata
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return onSubmit({
|
|
||||||
...data,
|
|
||||||
metadata,
|
|
||||||
privateMetadata
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Form onSubmit={handleSubmit} initial={initialData} confirmLeave>
|
|
||||||
{({ data, change, submit, hasChanged }) => {
|
|
||||||
const changeMetadata = makeMetadataChangeHandler(change);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<CategoryUpdateForm category={category} onSubmit={onSubmit}>
|
||||||
|
{({ data, change, handlers, submit, hasChanged }) => (
|
||||||
<Container>
|
<Container>
|
||||||
<AppHeader onBack={onBack}>
|
<AppHeader onBack={onBack}>
|
||||||
{intl.formatMessage(sectionNames.categories)}
|
{intl.formatMessage(sectionNames.categories)}
|
||||||
</AppHeader>
|
</AppHeader>
|
||||||
<PageHeader title={category ? category.name : undefined} />
|
<PageHeader title={category?.name} />
|
||||||
<CategoryDetailsForm
|
<CategoryDetailsForm
|
||||||
category={category}
|
|
||||||
data={data}
|
data={data}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
onChange={change}
|
onChange={change}
|
||||||
|
onDescriptionChange={handlers.changeDescription}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<CategoryBackground
|
<CategoryBackground
|
||||||
|
@ -191,7 +135,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<Metadata data={data} onChange={changeMetadata} />
|
<Metadata data={data} onChange={handlers.changeMetadata} />
|
||||||
<CardSpacer />
|
<CardSpacer />
|
||||||
<TabContainer>
|
<TabContainer>
|
||||||
<CategoriesTab
|
<CategoriesTab
|
||||||
|
@ -254,7 +198,7 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
)}
|
)}
|
||||||
{currentTab === CategoryPageTab.products && (
|
{currentTab === CategoryPageTab.products && (
|
||||||
<CategoryProducts
|
<CategoryProducts
|
||||||
categoryName={maybe(() => category.name)}
|
categoryName={category?.name}
|
||||||
products={products}
|
products={products}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
pageInfo={pageInfo}
|
pageInfo={pageInfo}
|
||||||
|
@ -277,9 +221,8 @@ export const CategoryUpdatePage: React.FC<CategoryUpdatePageProps> = ({
|
||||||
disabled={disabled || !hasChanged}
|
disabled={disabled || !hasChanged}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
)}
|
||||||
}}
|
</CategoryUpdateForm>
|
||||||
</Form>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
CategoryUpdatePage.displayName = "CategoryUpdatePage";
|
CategoryUpdatePage.displayName = "CategoryUpdatePage";
|
||||||
|
|
107
src/categories/components/CategoryUpdatePage/form.tsx
Normal file
107
src/categories/components/CategoryUpdatePage/form.tsx
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import { OutputData } from "@editorjs/editorjs";
|
||||||
|
import { CategoryDetails_category } from "@saleor/categories/types/CategoryDetails";
|
||||||
|
import { MetadataFormData } from "@saleor/components/Metadata";
|
||||||
|
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
|
||||||
|
import useForm, { FormChange } from "@saleor/hooks/useForm";
|
||||||
|
import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit";
|
||||||
|
import { mapMetadataItemToInput } from "@saleor/utils/maps";
|
||||||
|
import getMetadata from "@saleor/utils/metadata/getMetadata";
|
||||||
|
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||||
|
import useRichText from "@saleor/utils/richText/useRichText";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface CategoryUpdateFormData extends MetadataFormData {
|
||||||
|
backgroundImageAlt: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
seoTitle: string;
|
||||||
|
seoDescription: string;
|
||||||
|
}
|
||||||
|
export interface CategoryUpdateData extends CategoryUpdateFormData {
|
||||||
|
description: OutputData;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CategoryUpdateHandlers {
|
||||||
|
changeMetadata: FormChange;
|
||||||
|
changeDescription: RichTextEditorChange;
|
||||||
|
}
|
||||||
|
export interface UseCategoryUpdateFormResult {
|
||||||
|
change: FormChange;
|
||||||
|
data: CategoryUpdateData;
|
||||||
|
handlers: CategoryUpdateHandlers;
|
||||||
|
hasChanged: boolean;
|
||||||
|
submit: () => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CategoryUpdateFormProps {
|
||||||
|
children: (props: UseCategoryUpdateFormResult) => React.ReactNode;
|
||||||
|
category: CategoryDetails_category;
|
||||||
|
onSubmit: (data: CategoryUpdateData) => Promise<any[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useCategoryUpdateForm(
|
||||||
|
category: CategoryDetails_category,
|
||||||
|
onSubmit: (data: CategoryUpdateData) => Promise<any[]>
|
||||||
|
): UseCategoryUpdateFormResult {
|
||||||
|
const [changed, setChanged] = React.useState(false);
|
||||||
|
const triggerChange = () => setChanged(true);
|
||||||
|
|
||||||
|
const form = useForm<CategoryUpdateFormData>({
|
||||||
|
backgroundImageAlt: category?.backgroundImage?.alt || "",
|
||||||
|
metadata: category?.metadata?.map(mapMetadataItemToInput),
|
||||||
|
name: category?.name || "",
|
||||||
|
privateMetadata: category?.privateMetadata?.map(mapMetadataItemToInput),
|
||||||
|
seoDescription: category?.seoDescription || "",
|
||||||
|
seoTitle: category?.seoTitle || "",
|
||||||
|
slug: category?.slug || ""
|
||||||
|
});
|
||||||
|
const [description, changeDescription] = useRichText({
|
||||||
|
initial: category?.descriptionJson,
|
||||||
|
triggerChange
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
isMetadataModified,
|
||||||
|
isPrivateMetadataModified,
|
||||||
|
makeChangeHandler: makeMetadataChangeHandler
|
||||||
|
} = useMetadataChangeTrigger();
|
||||||
|
|
||||||
|
const handleChange: FormChange = (event, cb) => {
|
||||||
|
form.change(event, cb);
|
||||||
|
triggerChange();
|
||||||
|
};
|
||||||
|
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||||
|
|
||||||
|
// Need to make it function to always have description.current up to date
|
||||||
|
const getData = (): CategoryUpdateData => ({
|
||||||
|
...form.data,
|
||||||
|
...getMetadata(form.data, isMetadataModified, isPrivateMetadataModified),
|
||||||
|
description: description.current
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => handleFormSubmit(getData(), onSubmit, setChanged);
|
||||||
|
|
||||||
|
return {
|
||||||
|
change: handleChange,
|
||||||
|
data: getData(),
|
||||||
|
handlers: {
|
||||||
|
changeDescription,
|
||||||
|
changeMetadata
|
||||||
|
},
|
||||||
|
hasChanged: changed,
|
||||||
|
submit
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const CategoryUpdateForm: React.FC<CategoryUpdateFormProps> = ({
|
||||||
|
children,
|
||||||
|
category,
|
||||||
|
onSubmit
|
||||||
|
}) => {
|
||||||
|
const props = useCategoryUpdateForm(category, onSubmit);
|
||||||
|
|
||||||
|
return <form onSubmit={props.submit}>{children(props)}</form>;
|
||||||
|
};
|
||||||
|
|
||||||
|
CategoryUpdateForm.displayName = "CategoryUpdateForm";
|
||||||
|
export default CategoryUpdateForm;
|
Loading…
Reference in a new issue