Skip to content

Commit 7f245e1

Browse files
committed
chore: improve modal
1 parent f7c4f0a commit 7f245e1

File tree

3 files changed

+170
-93
lines changed

3 files changed

+170
-93
lines changed

src/ui/Modal/FormikModal.stories.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import React, { useState } from "react";
2-
import FormikModal from "./FormikModal";
3-
import { ComponentStory } from "@storybook/react";
41
import { FormikCodeEditor } from "@flanksource-ui/components/Forms/Formik/FormikCodeEditor";
52
import FormikTextInput from "@flanksource-ui/components/Forms/Formik/FormikTextInput";
3+
import { StoryFn } from "@storybook/react";
4+
import { useState } from "react";
5+
import FormikModal from "./FormikModal";
66

77
export default {
88
title: "FormikModal",
99
component: FormikModal
1010
};
1111

12-
const Template: ComponentStory<React.FC> = (arg) => {
12+
const Template: StoryFn<typeof FormikModal> = (arg) => {
1313
const [isOpen, setIsOpen] = useState(false);
1414
const handleClose = () => setIsOpen(false);
1515
return (
@@ -21,22 +21,24 @@ const Template: ComponentStory<React.FC> = (arg) => {
2121
>
2222
show modal
2323
</button>
24-
<FormikModal
25-
open={isOpen}
26-
onClose={handleClose}
27-
onDelete={(values) => console.log("deleted", values)}
28-
onSave={(values) => console.log("saved", values)}
29-
onBack={() => console.log('backed')}
30-
31-
{...arg}>
32-
</FormikModal>
24+
<FormikModal {...arg} onClose={handleClose} open={isOpen}></FormikModal>
3325
</>
3426
);
3527
};
3628

3729
export const Variant1 = Template.bind({});
3830
Variant1.args = {
3931
title: "Modal title",
32+
formikFormProps: {
33+
onDelete: (values) => console.log("deleted", values),
34+
onSave: (values) => console.log("saved", values),
35+
onBack: () => console.log("backed"),
36+
showClose: true,
37+
showDelete: true,
38+
showSave: true
39+
},
40+
size: "medium",
41+
showExpand: true,
4042
children: (
4143
<>
4244
<FormikTextInput
@@ -53,14 +55,12 @@ Variant1.args = {
5355
defaultValue="default"
5456
/>
5557

56-
5758
<FormikCodeEditor
5859
fieldName="chartValues"
5960
label="Values"
6061
lines={3}
6162
className="flex flex-col"
6263
/>
63-
6464
</>
6565
)
6666
};

src/ui/Modal/FormikModal.tsx

Lines changed: 154 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import { IModalProps, Modal, useDialogSize } from ".";
2-
import { Form, Formik } from "formik";
3-
import { Button } from "../Buttons/Button";
4-
import { Fragment, createContext, useContext, useEffect, useRef, useState } from "react";
51
import { Dialog, Transition } from "@headlessui/react";
62
import { XIcon } from "@heroicons/react/solid";
73
import clsx from "clsx";
4+
import { Form, Formik } from "formik";
5+
import { Fragment, createContext, useContext, useRef, useState } from "react";
86
import { BsArrowsFullscreen, BsFullscreenExit } from "react-icons/bs";
7+
import { IModalProps, useDialogSize } from ".";
8+
import { Button } from "../Buttons/Button";
99
import DialogButton from "../Buttons/DialogButton";
1010
import HelpLink from "../Buttons/HelpLink";
1111

12-
interface FormikModalProps extends IModalProps {
13-
initialValues?: Record<string, any>;
12+
interface FormikFormModalProps {
1413
showBack?: boolean;
1514
showDelete?: boolean;
1615
showSave?: boolean;
@@ -25,33 +24,73 @@ interface FormikModalProps extends IModalProps {
2524
onDelete?: (values: Record<string, any>) => void | Promise<any>;
2625
}
2726

28-
2927
export type FormikModalContextProps = {
30-
props: FormikModalProps;
31-
setProps: (props: FormikModalProps) => void;
28+
props: FormikFormModalProps;
29+
setProps: (props: FormikFormModalProps) => void;
3230
values: Record<string, any>;
33-
}
31+
};
3432

3533
export function useFormikModal() {
36-
let { props, setProps: set, values } = useContext(FormikModalContext)
37-
let setProps = (p: FormikModalProps) => {
38-
console.log(p, props)
39-
set(p)
40-
}
34+
const { props, setProps: set, values } = useContext(FormikModalContext);
35+
const setProps = (p: FormikFormModalProps) => {
36+
set(p);
37+
};
4138
return {
42-
props, setProps, values
43-
}
39+
props,
40+
setProps,
41+
values
42+
};
4443
}
4544

46-
export const FormikModalContext = createContext<FormikModalContextProps>(undefined!);
45+
export const FormikModalContext = createContext<FormikModalContextProps>(
46+
undefined!
47+
);
4748

48-
export default function FormikModal(initialProps: FormikModalProps) {
49-
console.log('initial', initialProps.open)
50-
const [props, setProps] = useState(initialProps);
51-
console.log('state', props.open)
52-
const sizeClass = useDialogSize(props.size);
53-
const formRef = useRef<HTMLFormElement>(null);
49+
type FormikModalProps = IModalProps & {
50+
formikFormProps: FormikFormModalProps;
51+
/**
52+
* Can only be used during the initial render of the modal, after that, you
53+
* can use Formik's setFieldValue to update the form values.
54+
*/
55+
initialValues?: Record<string, any>;
56+
};
57+
58+
export default function FormikModal({
59+
onClose = () => {},
60+
formikFormProps,
61+
size = "medium",
62+
helpLink,
63+
showExpand,
64+
bodyClass,
65+
initialValues,
66+
title,
67+
titleClass,
68+
hideCloseButton,
69+
childClassName,
70+
...props
71+
}: FormikModalProps) {
72+
const [formikProps, setFormikProps] = useState<FormikFormModalProps>(() => {
73+
// set default values for formikFormProps
74+
const {
75+
saveTitle = "Save",
76+
backTitle = "Back",
77+
deleteTitle = "Delete",
78+
closeTitle = "Close",
79+
...props
80+
} = formikFormProps;
5481

82+
return {
83+
...props,
84+
saveTitle,
85+
backTitle,
86+
deleteTitle,
87+
closeTitle
88+
};
89+
});
90+
91+
const [_size, _setSize] = useState(size);
92+
const sizeClass = useDialogSize(_size);
93+
const formRef = useRef<HTMLFormElement>(null);
5594

5695
const touchAllFormFields = (
5796
setFieldTouched: (
@@ -70,11 +109,19 @@ export default function FormikModal(initialProps: FormikModalProps) {
70109
as="div"
71110
auto-reopen="true"
72111
className={props.dialogClassName}
73-
onClose={props.allowBackgroundClose ? () => props.onClose?.() : () => { }}
112+
onClose={(e) => {
113+
if (props.allowBackgroundClose) {
114+
onClose();
115+
}
116+
}}
74117
{...props}
75118
>
76119
<div
77-
className={clsx("flex items-center justify-center mx-auto my-auto", sizeClass)} >
120+
className={clsx(
121+
"flex items-center justify-center mx-auto my-auto",
122+
sizeClass
123+
)}
124+
>
78125
{/* @ts-ignore */}
79126
<Transition.Child
80127
as={Fragment as any}
@@ -97,54 +144,66 @@ export default function FormikModal(initialProps: FormikModalProps) {
97144
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
98145
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
99146
>
100-
<div className={clsx(
101-
props.size !== "small" && props.childClassName,
102-
"mt-20 mb-10 justify-between overflow-auto bg-white rounded-lg text-left shadow-xl transform transition-all flex flex-col")}
147+
<div
148+
className={clsx(
149+
_size !== "small" && childClassName,
150+
"mt-20 mb-10 justify-between overflow-auto bg-white rounded-lg text-left shadow-xl transform transition-all flex flex-col"
151+
)}
103152
>
104153
<div className="py-4 px-4 gap-2 flex item-center rounded-t-lg justify-between bg-gray-100">
105154
<h1
106155
className={clsx(
107156
"font-semibold flex-1 overflow-x-auto text-lg",
108-
props.titleClass
157+
titleClass
109158
)}
110159
>
111-
{props.title}
160+
{title}
112161
</h1>
113162

114-
{props.helpLink && <HelpLink link={props.helpLink} />}
115-
{props.showExpand && props.size !== 'full' && props.size !== 'small' &&
116-
<DialogButton icon={BsArrowsFullscreen} onClick={() => setProps({
117-
...props,
118-
size: 'full'
119-
})} />}
163+
{helpLink && <HelpLink link={helpLink} />}
164+
{showExpand && _size !== "full" && _size !== "small" && (
165+
<DialogButton
166+
icon={BsArrowsFullscreen}
167+
onClick={() => _setSize("full")}
168+
/>
169+
)}
120170

121-
{props.showExpand && props.size === 'full' && <DialogButton icon={BsFullscreenExit} onClick={() => setProps({
122-
...props,
123-
size: 'medium'
124-
})} />}
171+
{showExpand && _size === "full" && (
172+
<DialogButton
173+
icon={BsFullscreenExit}
174+
onClick={() => _setSize("medium")}
175+
/>
176+
)}
125177

126-
{!props.hideCloseButton && <DialogButton icon={XIcon} onClick={props.onClose} />}
178+
{!hideCloseButton && (
179+
<DialogButton icon={XIcon} onClick={onClose} />
180+
)}
127181
</div>
128182

129-
<div className={clsx("flex flex-col flex-1 mb-auto ", props.bodyClass)} >
130-
183+
<div className={clsx("flex flex-col flex-1 mb-auto ", bodyClass)}>
131184
<Formik
132-
initialValues={props.initialValues!}
185+
initialValues={initialValues!}
133186
enableReinitialize={true}
134-
onSubmit={(values) => {
135-
if (props.onSave) {
136-
props.onSave(values);
187+
onSubmit={(values): void => {
188+
if (formikProps.onSave) {
189+
formikProps.onSave(values);
137190
}
138191
}}
139192
validateOnBlur
140193
validateOnChange
141194
>
142-
{({ handleSubmit, handleReset, setFieldTouched, validateField, values }) => (
143-
<FormikModalContext.Provider value={{ props, setProps, values }}>
195+
{({ handleSubmit, handleReset, setFieldTouched, values }) => (
196+
<FormikModalContext.Provider
197+
value={{
198+
props: formikProps,
199+
setProps: setFormikProps,
200+
values
201+
}}
202+
>
144203
<Form
145204
onSubmit={(e) => {
146205
handleSubmit(e);
147-
if (props.touchAllOnSubmit) {
206+
if (formikProps.touchAllOnSubmit) {
148207
touchAllFormFields(setFieldTouched);
149208
}
150209
}}
@@ -156,46 +215,64 @@ export default function FormikModal(initialProps: FormikModalProps) {
156215
{props.children}
157216
</div>
158217

159-
{(props.showBack || props.showSave || props.showDelete || props.showClose) &&
218+
{(formikProps.showBack ||
219+
formikProps.showSave ||
220+
formikProps.showDelete ||
221+
formikProps.showClose) && (
160222
<div className="flex flex-row bg-gray-100 p-4">
161223
<div className="flex flex-1 flex-row items-center space-x-4 ">
162-
{props.showBack && props.onBack && (
224+
{formikProps.showBack && formikProps.onBack && (
163225
<div className="flex flex-1 flex-row">
164-
<Button className="btn-default btn-btn-secondary-base btn-secondary"
226+
<Button
227+
className="btn-default btn-btn-secondary-base btn-secondary"
165228
onClick={(e) => {
166-
props.onBack?.()
167-
}}>{props.backTitle}</Button>
229+
formikProps.onBack?.();
230+
}}
231+
>
232+
{formikProps.backTitle}
233+
</Button>
168234
</div>
169235
)}
170-
{props.showDelete && props.onDelete &&
171-
<Button className="btn-default btn-btn-secondary-base btn-secondary"
172-
onClick={() => {
173-
props.onDelete?.(values)
174-
}}>{props.deleteTitle}</Button>
175-
}
176-
177-
{props.showClose && props.onClose &&
178-
<Button className="btn-primary"
179-
onClick={() => {
180-
props.onClose?.()
181-
}}>{props.closeTitle}</Button>
182-
}
183-
{props.showSave &&
184-
<Button type="submit" className="btn-default btn-primary">{props.saveTitle}</Button>
185-
}
236+
{formikProps.showDelete &&
237+
formikProps.onDelete && (
238+
<Button
239+
className="btn-default btn-btn-secondary-base btn-secondary"
240+
onClick={() => {
241+
formikProps.onDelete?.(values);
242+
}}
243+
>
244+
{formikProps.deleteTitle}
245+
</Button>
246+
)}
247+
248+
{formikProps.showClose && onClose && (
249+
<Button
250+
className="btn-primary"
251+
onClick={() => onClose()}
252+
>
253+
{formikProps.closeTitle}
254+
</Button>
255+
)}
256+
{formikProps.showSave && (
257+
<Button
258+
type="submit"
259+
className="btn-default btn-primary"
260+
>
261+
{formikProps.saveTitle}
262+
</Button>
263+
)}
186264
</div>
187265
</div>
188-
}
266+
)}
189267
</Form>
190268
</FormikModalContext.Provider>
191269
)}
192-
</Formik >
270+
</Formik>
193271
</div>
194-
195272
</div>
196273
</Transition.Child>
197274
</div>
198275
</Dialog>
199-
</Transition.Root >
200-
)
276+
</Transition.Root>
277+
);
201278
}

src/ui/Modal/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const ModalContext = createContext<ModalContextProps>(undefined!);
3434
*/
3535
export const modalHelpLinkAtom = atom<string | undefined>(undefined);
3636

37-
export type ModalSize = "small" | "medium" | "large" | "full";
37+
export type ModalSize = "very-small" | "small" | "medium" | "large" | "full";
3838

3939
export interface IModalProps {
4040
title?: React.ReactNode;

0 commit comments

Comments
 (0)