Skip to content

Commit

Permalink
reset comment form after successful submit
Browse files Browse the repository at this point in the history
  • Loading branch information
bilalabbad committed Aug 13, 2024
1 parent e456200 commit ec94cc8
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 106 deletions.
8 changes: 5 additions & 3 deletions frontend/app/src/components/conversations/add-comment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { DynamicFieldProps } from "@/components/form/type";
import { SCHEMA_ATTRIBUTE_KIND } from "@/config/constants";
import { useAuth } from "@/hooks/useAuth";
import { constructPath } from "@/utils/fetch";
import { ReactElement } from "react";
import { forwardRef, ReactElement } from "react";
import { useLocation } from "react-router-dom";
import { FormRef } from "@/components/ui/form";

const fields: Array<DynamicFieldProps> = [
{
Expand All @@ -29,13 +30,14 @@ type tAddComment = {
additionalButtons?: ReactElement;
};

export const AddComment = ({ onSubmit, onCancel }: tAddComment) => {
export const AddComment = forwardRef<FormRef, tAddComment>(({ onSubmit, onCancel }, ref) => {
const location = useLocation();
const { isAuthenticated } = useAuth();

if (isAuthenticated) {
return (
<DynamicForm
ref={ref}
fields={fields}
onCancel={onCancel}
onSubmit={async (data) => {
Expand All @@ -58,4 +60,4 @@ export const AddComment = ({ onSubmit, onCancel }: tAddComment) => {
to be able to add a comment.
</div>
);
};
});
109 changes: 57 additions & 52 deletions frontend/app/src/components/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { MarkdownViewer } from "./markdown-viewer";
type MarkdownEditorProps = {
className?: string;
defaultValue?: string;
value?: string;
disabled?: boolean;
onChange?: (value: string) => void;
placeholder?: string;
Expand All @@ -16,63 +17,67 @@ type MarkdownEditorProps = {
export const MarkdownEditor: FC<MarkdownEditorProps> = forwardRef<
HTMLButtonElement,
MarkdownEditorProps
>(({ id, className = "", defaultValue = "", disabled = false, onChange, placeholder }, ref) => {
const [isPreviewActive, setPreviewActive] = useState<boolean>(false);
const [editorText, setEditorText] = useState<string>(defaultValue);
const codeMirrorRef = useRef<HTMLDivElement>(null);
>(
(
{ value, id, className = "", defaultValue = "", disabled = false, onChange, placeholder },
ref
) => {
const [isPreviewActive, setPreviewActive] = useState<boolean>(false);
const codeMirrorRef = useRef<HTMLDivElement>(null);

const handleTextChange = (value: string) => {
setEditorText(value);
if (onChange) onChange(value);
};
const handleTextChange = (value: string) => {
if (onChange) onChange(value);
};

const codeMirror = useCodeMirror(codeMirrorRef.current, {
placeholder,
defaultValue,
onChange: handleTextChange,
});
const codeMirror = useCodeMirror(codeMirrorRef.current, {
placeholder,
defaultValue,
value,
onChange: handleTextChange,
});

if (disabled) {
return (
<MarkdownViewer
markdownText={editorText}
className="w-full bg-gray-100 min-h-10 rounded-md p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 cursor-not-allowed"
/>
);
}

return (
<>
{id && (
<button
id={id}
ref={ref}
type="button"
onClick={() => codeMirror.view?.focus()} // for E2E
onFocus={() => codeMirror.view?.focus()}
className="w-0 h-0"
if (disabled) {
return (
<MarkdownViewer
markdownText={codeMirror.view?.state?.doc.toString()}
className="w-full bg-gray-100 min-h-10 rounded-md p-2 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 cursor-not-allowed"
/>
)}
<div
className={classNames(
`
);
}

return (
<>
{id && (
<button
id={id}
ref={ref}
type="button"
onClick={() => codeMirror.view?.focus()} // for E2E
onFocus={() => codeMirror.view?.focus()}
className="w-0 h-0"
/>
)}
<div
className={classNames(
`
bg-white rounded-md border border-gray-300 shadow-sm
focus-within:outline focus-within:outline-custom-blue-600 focus-within:border-custom-blue-600
`,
className
)}>
<MarkdownEditorHeader
codeMirror={codeMirror}
previewMode={isPreviewActive}
onPreviewToggle={() => setPreviewActive((prev) => !prev)}
/>
className
)}>
<MarkdownEditorHeader
codeMirror={codeMirror}
previewMode={isPreviewActive}
onPreviewToggle={() => setPreviewActive((prev) => !prev)}
/>

{isPreviewActive ? (
<MarkdownViewer markdownText={editorText} className="p-2" />
) : (
<div ref={codeMirrorRef} data-cy="codemirror-editor" data-testid="codemirror-editor" />
)}
</div>
</>
);
});
{isPreviewActive ? (
<MarkdownViewer markdownText={codeMirror.view?.state?.doc.toString()} className="p-2" />
) : (
<div ref={codeMirrorRef} data-cy="codemirror-editor" data-testid="codemirror-editor" />
)}
</div>
</>
);
}
);
47 changes: 25 additions & 22 deletions frontend/app/src/components/form/dynamic-form.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from "@/components/buttons/button-primitive";
import { Form, FormProps, FormSubmit } from "@/components/ui/form";
import { Form, FormProps, FormRef, FormSubmit } from "@/components/ui/form";
import { DynamicFieldProps } from "@/components/form/type";
import { SCHEMA_ATTRIBUTE_KIND } from "@/config/constants";
import ColorField from "@/components/form/fields/color.field";
Expand All @@ -14,37 +14,40 @@ import TextareaField from "@/components/form/fields/textarea.field";
import RelationshipField from "@/components/form/fields/relationship.field";
import { warnUnexpectedType } from "@/utils/common";
import EnumField from "@/components/form/fields/enum.field";
import { forwardRef } from "react";

export interface DynamicFormProps extends FormProps {
fields: Array<DynamicFieldProps>;
onCancel?: () => void;
submitLabel?: string;
}

const DynamicForm = ({ fields, onCancel, submitLabel, ...props }: DynamicFormProps) => {
const formDefaultValues = fields.reduce(
(acc, field) => ({ ...acc, [field.name]: field.defaultValue }),
{}
);
const DynamicForm = forwardRef<FormRef, DynamicFormProps>(
({ fields, onCancel, submitLabel, ...props }, ref) => {
const formDefaultValues = fields.reduce(
(acc, field) => ({ ...acc, [field.name]: field.defaultValue }),
{}
);

return (
<Form {...props} defaultValues={formDefaultValues}>
{fields.map((field) => (
<DynamicInput key={field.name} {...field} />
))}
return (
<Form ref={ref} {...props} defaultValues={formDefaultValues}>
{fields.map((field) => (
<DynamicInput key={field.name} {...field} />
))}

<div className="text-right">
{onCancel && (
<Button variant="outline" className="mr-2" onClick={onCancel}>
Cancel
</Button>
)}
<div className="text-right">
{onCancel && (
<Button variant="outline" className="mr-2" onClick={onCancel}>
Cancel
</Button>
)}

<FormSubmit>{submitLabel ?? "Save"}</FormSubmit>
</div>
</Form>
);
};
<FormSubmit>{submitLabel ?? "Save"}</FormSubmit>
</div>
</Form>
);
}
);

const DynamicInput = (props: DynamicFieldProps) => {
const { type, ...otherProps } = props;
Expand Down
60 changes: 33 additions & 27 deletions frontend/app/src/components/ui/form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Button, ButtonProps } from "@/components/buttons/button-primitive";
import { classNames } from "@/utils/common";
import * as LabelPrimitive from "@radix-ui/react-label";
import { Slot } from "@radix-ui/react-slot";
import React, {
createContext,
Expand All @@ -9,6 +8,7 @@ import React, {
useContext,
useEffect,
useId,
useImperativeHandle,
} from "react";
import {
Controller,
Expand All @@ -18,39 +18,45 @@ import {
useFormContext,
} from "react-hook-form";
import { Spinner } from "@/components/ui/spinner";
import Label from "@/components/ui/label";
import Label, { LabelProps } from "@/components/ui/label";

export type FormRef = ReturnType<typeof useForm>;

export interface FormProps extends Omit<FormHTMLAttributes<HTMLFormElement>, "onSubmit"> {
onSubmit?: (v: Record<string, unknown>) => Promise<void>;
defaultValues?: Partial<Record<string, unknown>>;
}

export const Form = ({ defaultValues, className, children, onSubmit, ...props }: FormProps) => {
const form = useForm({ defaultValues });
export const Form = React.forwardRef<FormRef, FormProps>(
({ defaultValues, className, children, onSubmit, ...props }, ref) => {
const form = useForm({ defaultValues });

useEffect(() => {
form.reset(defaultValues);
}, [JSON.stringify(defaultValues)]);
useImperativeHandle(ref, () => form, []);

return (
<FormProvider {...form}>
<form
onSubmit={(event) => {
if (event && event.stopPropagation) {
event.stopPropagation();
}

if (!onSubmit) return;

form.handleSubmit(onSubmit)(event);
}}
className={classNames("space-y-4", className)}
{...props}>
{children}
</form>
</FormProvider>
);
};
useEffect(() => {
form.reset(defaultValues);
}, [JSON.stringify(defaultValues)]);

return (
<FormProvider {...form}>
<form
onSubmit={(event) => {
if (event && event.stopPropagation) {
event.stopPropagation();
}

if (!onSubmit) return;

form.handleSubmit(onSubmit)(event);
}}
className={classNames("space-y-4", className)}
{...props}>
{children}
</form>
</FormProvider>
);
}
);

type FormFieldContextType = { id: string; name: string };
const FormFieldContext = createContext<FormFieldContextType>({} as FormFieldContextType);
Expand All @@ -66,7 +72,7 @@ export const FormField = (props: ControllerProps) => {
);
};

export const FormLabel = ({ ...props }: LabelPrimitive.LabelProps) => {
export const FormLabel = ({ ...props }: LabelProps) => {
const { id } = useContext(FormFieldContext);

return <Label htmlFor={id} {...props} />;
Expand Down
15 changes: 15 additions & 0 deletions frontend/app/src/hooks/useCodeMirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const theme = EditorView.baseTheme({

type CodeMirrorProps = {
defaultValue?: string;
value?: string;
placeholder?: string;
onChange?: (value: string) => void;
lang?: "markdown" | "graphql";
Expand All @@ -47,6 +48,7 @@ type CodeMirrorProps = {
export function useCodeMirror(
container: HTMLDivElement | null,
{
value,
defaultValue = "",
onChange,
placeholder = "",
Expand All @@ -64,6 +66,19 @@ export function useCodeMirror(
}
});

useEffect(() => {
if (!view) return;

const currentValue = view.state.doc.toString();
const newValue = value ?? "";

if (value === currentValue) return;

view.dispatch({
changes: { from: 0, to: currentValue.length, insert: newValue || "" },
});
}, [value, view]);

useEffect(() => {
if (containerElement && !state) {
const langExtensions =
Expand Down
Loading

0 comments on commit ec94cc8

Please sign in to comment.