Skip to content

Commit

Permalink
fix(uniform): improve form debug disable logic, add setting for custo…
Browse files Browse the repository at this point in the history
…m ls key
  • Loading branch information
toxsick committed Nov 11, 2024
1 parent 5778ff7 commit 23345dc
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 11 deletions.
10 changes: 6 additions & 4 deletions packages/uniform/src/Form/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { VetoInstance } from '@fuf-stack/veto';
import type { ReactNode } from 'react';
import type { FieldValues, SubmitHandler } from 'react-hook-form';
import type { DebugModeSettings } from './subcomponents/FormContext';

import { cn } from '@fuf-stack/pixel-utils';

Expand All @@ -13,8 +14,8 @@ export interface FormProps {
children: ReactNode | ReactNode[];
/** CSS class name */
className?: string | string[];
/** disable form debug completely */
disableDebug?: boolean;
/** settings for from debug mode */
debug?: DebugModeSettings;
/** initial form values */
initialValues?: FieldValues;
/** name of the form */
Expand All @@ -35,7 +36,7 @@ export interface FormProps {
const Form = ({
children,
className = undefined,
disableDebug = false,
debug = undefined,
initialValues = undefined,
name = undefined,
onSubmit,
Expand All @@ -45,6 +46,7 @@ const Form = ({
}: FormProps) => {
return (
<FormProvider
debugModeSettings={debug}
initialValues={initialValues}
onSubmit={onSubmit}
validation={validation}
Expand All @@ -60,7 +62,7 @@ const Form = ({
>
{children}
</form>
{!disableDebug && <FormDebugViewer className="w-96 flex-shrink" />}
{!debug?.disable && <FormDebugViewer className="w-96 flex-shrink" />}
</div>
)}
</FormProvider>
Expand Down
26 changes: 20 additions & 6 deletions packages/uniform/src/Form/subcomponents/FormContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ export const removeNullishFields = (obj: Record<string, unknown>) => {
);
};

type DebugMode = 'debug' | 'debug-testids' | 'off';
type DebugMode = 'debug' | 'debug-testids' | 'off' | 'disabled';

export type DebugModeSettings = {
/** disable form debug completely */
disable?: boolean;
/** custom localStorage key to save debug mode state */
localStorageKey?: string;
};

const DEBUG_MODE_LOCAL_STORAGE_KEY_DEFAULT = 'uniform:debug-mode';

/**
* The `UniformContext` provides control over the form's submission behavior and may optionally include
Expand All @@ -37,6 +46,8 @@ type DebugMode = 'debug' | 'debug-testids' | 'off';
export const UniformContext = React.createContext<{
/** Form debug mode enabled or not */
debugMode: DebugMode;
/** settings for from debug mode */
debugModeSettings?: DebugModeSettings;
/** Function to update if the form can currently be submitted */
preventSubmit: (prevent: boolean) => void;
/** Setter to enable or disable form debug mode */
Expand All @@ -56,6 +67,8 @@ interface FormProviderProps {
children: (childProps: {
handleSubmit: (e?: React.BaseSyntheticEvent) => Promise<void>;
}) => ReactNode;
/** settings for from debug mode */
debugModeSettings?: DebugModeSettings;
/** initial form values */
initialValues?: FieldValues;
/** form submit handler */
Expand All @@ -66,8 +79,6 @@ interface FormProviderProps {
validationTrigger: 'onChange' | 'onBlur' | 'onSubmit' | 'onTouched' | 'all';
}

const LOCALSTORAGE_DEBUG_MODE_KEY = 'uniform:debug-mode';

/**
* FormProvider component provides:
* - The veto validation schema context
Expand All @@ -77,6 +88,7 @@ const LOCALSTORAGE_DEBUG_MODE_KEY = 'uniform:debug-mode';
*/
const FormProvider: React.FC<FormProviderProps> = ({
children,
debugModeSettings = undefined,
initialValues = undefined,
onSubmit,
validation = undefined,
Expand All @@ -87,22 +99,24 @@ const FormProvider: React.FC<FormProviderProps> = ({

// Form Debug mode state is handled in the form context
const [debugMode, setDebugMode] = useLocalStorage<DebugMode>(
LOCALSTORAGE_DEBUG_MODE_KEY,
debugModeSettings?.localStorageKey || DEBUG_MODE_LOCAL_STORAGE_KEY_DEFAULT,
'off',
);

// Memoize the context value to prevent re-renders
const contextValue = useMemo(
() => ({
debugMode,
// set debugMode to disabled when debugModeSettings.disable is true
// otherwise use current debug mode from localStorage
debugMode: debugModeSettings?.disable ? 'disabled' : debugMode,
preventSubmit: (prevent: boolean) => {
setPreventSubmit(prevent);
},
setDebugMode,
validation,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[debugMode],
[debugMode, debugModeSettings?.disable],
);

// Initialize react hook form
Expand Down
21 changes: 20 additions & 1 deletion packages/uniform/src/_examples/Examples.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ export const AllFieldRenderers: Story = {
validation,
children: (
<Card
footer={<SubmitButton />}
className={{ footer: 'flex-row-reverse' }}
footer={<SubmitButton />}
>
<Grid className="md:grid-cols-2">
<Input
Expand Down Expand Up @@ -136,3 +136,22 @@ export const AllFieldRenderers: Story = {
),
},
};

export const DebugModeDisabled: Story = {
args: {
debug: { disable: true },
children: (
<Card
className={{ footer: 'flex-row-reverse' }}
footer={<SubmitButton />}
>
<Input
name="stringField"
label="String Field"
placeholder="String Field..."
/>
</Card>
),
validation,
},
};
95 changes: 95 additions & 0 deletions packages/uniform/src/_examples/__snapshots__/Examples.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -887,3 +887,98 @@ exports[`Story Snapshots > AllFieldRenderers 1`] = `
</div>
</div>
`;

exports[`Story Snapshots > DebugModeDisabled 1`] = `
<div>
<div
class="h-full w-full p-5"
>
<div
class="mx-auto xl:w-1/2"
>
<div
class="flex w-full flex-row justify-between gap-6"
>
<form
class="flex-grow"
data-testid=""
>
<div
class="flex flex-col relative overflow-hidden h-auto text-foreground box-border bg-content1 outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 shadow-none rounded-small w-full transition-transform-background motion-reduce:transition-none border border-divider"
tabindex="-1"
>
<div
class="relative flex w-full p-3 flex-auto flex-col place-content-inherit align-items-inherit h-auto break-words text-left overflow-y-auto subpixel-antialiased"
>
<div
class="group flex flex-col data-[hidden=true]:hidden w-full relative justify-end data-[has-label=true]:mt-[calc(theme(fontSize.small)_+_10px)]"
data-filled="true"
data-filled-within="true"
data-has-elements="true"
data-has-label="true"
data-has-value="true"
data-required="true"
data-slot="base"
>
<div
class="h-full flex flex-col"
data-slot="main-wrapper"
>
<div
class="relative w-full inline-flex tap-highlight-transparent flex-row items-center shadow-sm px-3 gap-3 border-medium border-default-200 data-[hover=true]:border-default-400 h-10 min-h-10 rounded-small transition-background !duration-150 transition-colors motion-reduce:transition-none group-data-[focus=true]:border-focus"
data-slot="input-wrapper"
style="cursor: text;"
>
<label
class="absolute pointer-events-none origin-top-left rtl:origin-top-right subpixel-antialiased block text-foreground-500 after:content-['*'] after:text-danger after:ml-0.5 rtl:after:ml-[unset] rtl:after:mr-0.5 will-change-auto !duration-200 !ease-out motion-reduce:transition-none transition-[transform,color,left,opacity] group-data-[filled-within=true]:text-foreground group-data-[filled-within=true]:pointer-events-auto pb-0 z-20 top-1/2 -translate-y-1/2 group-data-[filled-within=true]:start-0 start-3 end-auto text-small group-data-[filled-within=true]:-translate-y-[calc(100%_+_theme(fontSize.small)/2_+_20px)] pe-2 max-w-full text-ellipsis overflow-hidden"
data-slot="label"
for="react-aria-react-useId-mock"
id="react-aria-react-useId-mock"
>
String Field
</label>
<div
class="inline-flex w-full items-center h-full box-border"
data-slot="inner-wrapper"
>
<input
aria-describedby="react-aria-react-useId-mock react-aria-react-useId-mock"
aria-label="String Field..."
aria-labelledby="react-aria-react-useId-mock"
aria-required="true"
class="w-full font-normal bg-transparent !outline-none placeholder:text-foreground-500 focus-visible:outline-none data-[has-start-content=true]:ps-1.5 data-[has-end-content=true]:pe-1.5 file:cursor-pointer file:bg-transparent file:border-0 autofill:bg-transparent bg-clip-text text-small"
data-slot="input"
data-testid="stringfield"
id="react-aria-react-useId-mock"
name="stringField"
placeholder="String Field..."
type="text"
value=""
/>
</div>
</div>
</div>
</div>
</div>
<hr
class="shrink-0 bg-divider border-none w-full h-divider my-0 border-divider"
role="separator"
/>
<div
class="p-3 h-auto flex w-full items-center overflow-hidden color-inherit subpixel-antialiased rounded-b-small flex-row-reverse"
>
<button
class="z-0 group relative inline-flex items-center justify-center box-border appearance-none select-none whitespace-nowrap font-normal subpixel-antialiased overflow-hidden tap-highlight-transparent data-[pressed=true]:scale-[0.97] outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2 px-4 min-w-20 h-10 text-small gap-2 rounded-medium [&>svg]:max-w-[theme(spacing.8)] transition-transform-colors-opacity motion-reduce:transition-none bg-success data-[hover=true]:opacity-hover text-white"
data-testid="form_submit_button"
type="submit"
>
Submit
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
`;

0 comments on commit 23345dc

Please sign in to comment.