wip
This commit is contained in:
parent
88bd52763c
commit
437df6fe9a
6 changed files with 146 additions and 42 deletions
|
@ -19,6 +19,7 @@ export interface RichTextEditorProps {
|
|||
label: string;
|
||||
name: string;
|
||||
onChange: RichTextEditorChange;
|
||||
onReady?: () => void;
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
|
@ -86,7 +87,10 @@ const useStyles = makeStyles(
|
|||
border: `1px solid ${fade(theme.palette.text.secondary, 0.4)}`,
|
||||
borderRadius: 4,
|
||||
boxShadow: `inset 0 0 0 0 ${theme.palette.primary.main}`,
|
||||
fontSize: theme.typography.body1.fontSize,
|
||||
padding: theme.spacing(3, 2),
|
||||
paddingBottom: theme.spacing(),
|
||||
paddingLeft: 10,
|
||||
position: "relative",
|
||||
transition: theme.transitions.duration.short + "ms"
|
||||
},
|
||||
|
@ -103,7 +107,8 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({
|
|||
error,
|
||||
helperText,
|
||||
label,
|
||||
onChange
|
||||
onChange,
|
||||
onReady
|
||||
}) => {
|
||||
const classes = useStyles({});
|
||||
|
||||
|
@ -120,6 +125,7 @@ const RichTextEditor: React.FC<RichTextEditorProps> = ({
|
|||
const savedData = await api.saver.save();
|
||||
onChange(savedData);
|
||||
},
|
||||
onReady,
|
||||
tools: {
|
||||
header: {
|
||||
class: Header,
|
||||
|
|
|
@ -86,12 +86,6 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
const intl = useIntl();
|
||||
const localizeDate = useDateLocalize();
|
||||
|
||||
// Ensures that it will not change after component rerenders, because it
|
||||
// generates different block keys and it causes editor to lose its content.
|
||||
const initialDescription = React.useRef(
|
||||
convertToRaw(ContentState.createFromText(""))
|
||||
);
|
||||
|
||||
// Display values
|
||||
const [selectedCategory, setSelectedCategory] = useStateFromProps(
|
||||
initial?.category || ""
|
||||
|
@ -144,8 +138,8 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
data={data}
|
||||
disabled={disabled}
|
||||
errors={errors}
|
||||
initialDescription={initialDescription.current}
|
||||
onChange={change}
|
||||
onDescriptionChange={handlers.changeDescription}
|
||||
/>
|
||||
<CardSpacer />
|
||||
{data.attributes.length > 0 && (
|
||||
|
@ -167,6 +161,7 @@ export const ProductCreatePage: React.FC<ProductCreatePageProps> = ({
|
|||
weightUnit={weightUnit}
|
||||
onChange={change}
|
||||
/>
|
||||
<CardSpacer />
|
||||
<ProductPricing
|
||||
currency={currency}
|
||||
data={data}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { OutputData } from "@editorjs/editorjs";
|
||||
import { MetadataFormData } from "@saleor/components/Metadata";
|
||||
import { MultiAutocompleteChoiceType } from "@saleor/components/MultiAutocompleteSelectField";
|
||||
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
|
||||
import { SingleAutocompleteChoiceType } from "@saleor/components/SingleAutocompleteSelectField";
|
||||
import useForm, { FormChange } from "@saleor/hooks/useForm";
|
||||
import useFormset, { FormsetChange } from "@saleor/hooks/useFormset";
|
||||
|
@ -17,7 +19,7 @@ import { SearchWarehouses_search_edges_node } from "@saleor/searches/types/Searc
|
|||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import { RawDraftContentState } from "draft-js";
|
||||
import useRichText from "@saleor/utils/richText/useRichText";
|
||||
import React from "react";
|
||||
|
||||
import { SearchProductTypes_search_edges_node } from "../../../searches/types/SearchProductTypes";
|
||||
|
@ -34,7 +36,7 @@ export interface ProductCreateFormData extends MetadataFormData {
|
|||
changeTaxCode: boolean;
|
||||
chargeTaxes: boolean;
|
||||
collections: string[];
|
||||
description: RawDraftContentState;
|
||||
description: OutputData;
|
||||
isAvailable: boolean;
|
||||
isAvailableForPurchase: boolean;
|
||||
isPublished: boolean;
|
||||
|
@ -56,19 +58,22 @@ export interface ProductCreateData extends ProductCreateFormData {
|
|||
stocks: ProductStockInput[];
|
||||
}
|
||||
|
||||
type ProductCreateHandlers = Record<
|
||||
interface ProductCreateHandlers
|
||||
extends Record<
|
||||
| "changeMetadata"
|
||||
| "selectCategory"
|
||||
| "selectCollection"
|
||||
| "selectProductType"
|
||||
| "selectTaxRate",
|
||||
FormChange
|
||||
> &
|
||||
>,
|
||||
Record<
|
||||
"changeStock" | "selectAttribute" | "selectAttributeMultiple",
|
||||
FormsetChange<string>
|
||||
> &
|
||||
Record<"addStock" | "deleteStock", (id: string) => void>;
|
||||
>,
|
||||
Record<"addStock" | "deleteStock", (id: string) => void> {
|
||||
changeDescription: RichTextEditorChange;
|
||||
}
|
||||
export interface UseProductCreateFormResult {
|
||||
change: FormChange;
|
||||
data: ProductCreateData;
|
||||
|
@ -106,7 +111,7 @@ const defaultInitialFormData: ProductCreateFormData &
|
|||
changeTaxCode: false,
|
||||
chargeTaxes: false,
|
||||
collections: [],
|
||||
description: {} as any,
|
||||
description: null,
|
||||
isAvailable: false,
|
||||
isAvailableForPurchase: false,
|
||||
isPublished: false,
|
||||
|
@ -117,7 +122,7 @@ const defaultInitialFormData: ProductCreateFormData &
|
|||
publicationDate: "",
|
||||
seoDescription: "",
|
||||
seoTitle: "",
|
||||
sku: null,
|
||||
sku: "",
|
||||
slug: "",
|
||||
stockQuantity: null,
|
||||
taxCode: null,
|
||||
|
@ -152,6 +157,10 @@ function useProductCreateForm(
|
|||
const [productType, setProductType] = useStateFromProps<ProductType>(
|
||||
initialProductType || null
|
||||
);
|
||||
const [description, changeDescription] = useRichText({
|
||||
initial: null,
|
||||
triggerChange
|
||||
});
|
||||
|
||||
const {
|
||||
makeChangeHandler: makeMetadataChangeHandler
|
||||
|
@ -211,19 +220,21 @@ function useProductCreateForm(
|
|||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||
|
||||
const data: ProductCreateData = {
|
||||
const getData = (): ProductCreateData => ({
|
||||
...form.data,
|
||||
attributes: attributes.data,
|
||||
description: description.current,
|
||||
productType,
|
||||
stocks: stocks.data
|
||||
};
|
||||
const submit = () => onSubmit(data);
|
||||
});
|
||||
const submit = () => onSubmit(getData());
|
||||
|
||||
return {
|
||||
change: handleChange,
|
||||
data,
|
||||
data: getData(),
|
||||
handlers: {
|
||||
addStock: handleStockAdd,
|
||||
changeDescription,
|
||||
changeMetadata,
|
||||
changeStock: handleStockChange,
|
||||
deleteStock: handleStockDelete,
|
||||
|
|
|
@ -23,6 +23,7 @@ import handleFormSubmit from "@saleor/utils/handlers/handleFormSubmit";
|
|||
import createMultiAutocompleteSelectHandler from "@saleor/utils/handlers/multiAutocompleteSelectChangeHandler";
|
||||
import createSingleAutocompleteSelectHandler from "@saleor/utils/handlers/singleAutocompleteSelectChangeHandler";
|
||||
import useMetadataChangeTrigger from "@saleor/utils/metadata/useMetadataChangeTrigger";
|
||||
import useRichText from "@saleor/utils/richText/useRichText";
|
||||
import { diff } from "fast-array-diff";
|
||||
import React from "react";
|
||||
|
||||
|
@ -164,15 +165,10 @@ function useProductUpdateForm(
|
|||
);
|
||||
const attributes = useFormset(getAttributeInputFromProduct(product));
|
||||
const stocks = useFormset(getStockInputFromProduct(product));
|
||||
const description = React.useRef<OutputData>();
|
||||
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
description.current = JSON.parse(product.descriptionJson);
|
||||
} catch {
|
||||
description.current = undefined;
|
||||
}
|
||||
}, [product]);
|
||||
const [description, changeDescription] = useRichText({
|
||||
initial: product?.descriptionJson,
|
||||
triggerChange
|
||||
});
|
||||
|
||||
const {
|
||||
isMetadataModified,
|
||||
|
@ -227,10 +223,6 @@ function useProductUpdateForm(
|
|||
opts.taxTypes
|
||||
);
|
||||
const changeMetadata = makeMetadataChangeHandler(handleChange);
|
||||
const changeDescription: RichTextEditorChange = data => {
|
||||
triggerChange();
|
||||
description.current = data;
|
||||
};
|
||||
|
||||
const data: ProductUpdateData = {
|
||||
...form.data,
|
||||
|
|
72
src/utils/richText/useRichText.test.ts
Normal file
72
src/utils/richText/useRichText.test.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { OutputData } from "@editorjs/editorjs";
|
||||
import { renderHook } from "@testing-library/react-hooks";
|
||||
|
||||
import useRichText from "./useRichText";
|
||||
|
||||
type Fixtures = Record<"short" | "long", OutputData>;
|
||||
const fixtures: Fixtures = {
|
||||
long: {
|
||||
blocks: [
|
||||
{
|
||||
data: {
|
||||
level: 1,
|
||||
text: "Some header"
|
||||
},
|
||||
type: "header"
|
||||
},
|
||||
{
|
||||
data: {
|
||||
text: "Some text"
|
||||
},
|
||||
type: "paragraph"
|
||||
}
|
||||
]
|
||||
},
|
||||
short: {
|
||||
blocks: [
|
||||
{
|
||||
data: {
|
||||
text: "Some text"
|
||||
},
|
||||
type: "paragraph"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
describe("useRichText", () => {
|
||||
it("properly saves data in form", () => {
|
||||
const triggerChange = jest.fn();
|
||||
const hook = renderHook(() =>
|
||||
useRichText({
|
||||
triggerChange
|
||||
})
|
||||
);
|
||||
|
||||
const [data, change] = hook.result.current;
|
||||
expect(data.current).toBe(undefined);
|
||||
|
||||
change(fixtures.short);
|
||||
|
||||
expect(data.current).toMatchObject(fixtures.short);
|
||||
expect(triggerChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("properly updates data in form", () => {
|
||||
const triggerChange = jest.fn();
|
||||
const hook = renderHook(() =>
|
||||
useRichText({
|
||||
initial: JSON.stringify(fixtures.short),
|
||||
triggerChange
|
||||
})
|
||||
);
|
||||
|
||||
const [data, change] = hook.result.current;
|
||||
expect(data.current).toMatchObject(fixtures.short);
|
||||
|
||||
change(fixtures.long);
|
||||
|
||||
expect(data.current).toMatchObject(fixtures.long);
|
||||
expect(triggerChange).toHaveBeenCalled();
|
||||
});
|
||||
});
|
28
src/utils/richText/useRichText.ts
Normal file
28
src/utils/richText/useRichText.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { OutputData } from "@editorjs/editorjs";
|
||||
import { RichTextEditorChange } from "@saleor/components/RichTextEditor";
|
||||
import { MutableRefObject, useEffect, useRef } from "react";
|
||||
|
||||
function useRichText(opts: {
|
||||
initial?: string | null;
|
||||
triggerChange: () => void;
|
||||
}): [MutableRefObject<OutputData>, RichTextEditorChange] {
|
||||
const data = useRef<OutputData>(
|
||||
opts.initial === null ? { blocks: [] } : undefined
|
||||
);
|
||||
useEffect(() => {
|
||||
try {
|
||||
data.current = JSON.parse(opts.initial);
|
||||
} catch {
|
||||
data.current = undefined;
|
||||
}
|
||||
}, [opts.initial]);
|
||||
|
||||
const change: RichTextEditorChange = newData => {
|
||||
opts.triggerChange();
|
||||
data.current = newData;
|
||||
};
|
||||
|
||||
return [data, change];
|
||||
}
|
||||
|
||||
export default useRichText;
|
Loading…
Reference in a new issue