---
name: migrate-drawer-to-drawer-layer
description: Guide migration of a drawer from useDrawer (or similar) to DrawerLayer using Composition + useDrawerLayer. Use when asked to "migrar drawer a DrawerLayer", "refactorizar drawer", or when replacing legacy drawer usage with the layer pattern.
---

# Skill: Migrate drawer to DrawerLayer

This skill guides the migration of an existing drawer (e.g. opened via `useDrawer` from material-hu) to the **DrawerLayer** API using the **Composition** pattern and **useDrawerLayer**. It documents the decisions taken in the StepActionList migration so they can be reused consistently.

**Reference implementation:** `.refinements/REFINEMENT-StepActionList-DrawerLayer.md`

---

## When to use

- User asks to migrate a drawer to DrawerLayer or to refactor drawer usage
- Code uses `useDrawer(Component, drawerProps, extraProps)` and renders a drawer node in JSX
- You want to centralize drawer opening via the layer and simplify the opener component

---

## Key decision: Composition + useDrawerLayer vs useDrawerLayerItem

Before implementing, **decide which pattern fits** the drawer you are migrating. This choice drives the rest of the refactor.

### Question to ask (for the dev or for the design)

> "Identifiqué que este drawer se puede migrar al DrawerLayer y la lógica se podría simplificar si usamos **Composition + useDrawerLayer** en lugar de **useDrawerLayerItem**. ¿Te parece bien que sigamos por ese camino?"

### When to use **Composition + useDrawerLayer** (recommended for form-like drawers)

Use when:

- The **content** of the drawer (e.g. a form) already owns all state that affects the drawer UI: loading/submitting, title, primary/secondary actions, close behavior.
- The **opener** (the component that opens the drawer) only needs to open it with the right content; it does **not** need to update the drawer’s config (title, button loading, etc.) from outside while the drawer is open.
- You are fine with the content rendering its own chrome: header, body, actions (via `Drawer.Header`, `Drawer.Body`, `Drawer.Actions`).

**Why:** The opener just calls `openDrawer({ content: <FormProvider />, wrapperProps })`. The form (or whatever content) uses `useDrawerLayer().closeDrawer()` and its own state for buttons and header. No reactive config, no `id`, no `updateDrawerConfig`. Simple and aligned with “open a self-contained content that closes itself.”

### When to use **useDrawerLayerItem** instead

Use when:

- The **opener** (or a parent) must **drive** the drawer’s config reactively while it is open (e.g. title or button loading that comes from state **outside** the drawer content).
- You need a stable drawer **id** and `updateDrawerConfig` so the layer reflects external state changes (e.g. step wizard title, or submit state owned by a parent).

**Why:** `useDrawerLayerItem(id, config)` keeps a reactive `config` in sync; the layer re-renders the drawer when that config changes. Use it when the “owner” of the drawer state is outside the content.

### Summary table

| Criterio | Composition + useDrawerLayer | useDrawerLayerItem |
|----------|-----------------------------|--------------------|
| Estado que afecta título/botones/loading | Dentro del contenido del drawer | Fuera (opener o padre) |
| ¿Quién cierra el drawer? | El contenido con `closeDrawer()` | Idem (o config.onClose) |
| ¿Quién actualiza título/loading mientras está abierto? | Nadie desde fuera; el contenido | El opener/padre vía updateDrawerConfig |
| Complejidad | Menor (una llamada openDrawer) | Mayor (id, config, updateConfig) |

For most “form in a drawer” cases, **Composition + useDrawerLayer** is the right choice.

---

## Migration steps (Composition + useDrawerLayer)

Apply these in order; adjust to the specific drawer (single form vs multiple types, etc.).

### 1. Hook for data only

- Identify what the current “wrapper” or parent does before rendering the form (e.g. fetch entity, build default values).
- Extract a **data hook** (e.g. `useXFormData(ids…)`) that returns `{ defaultValues, entity, isLoading }`.
- The hook should **not** render UI; only load and shape data for the form.

### 2. Provider: data + default values only

- The **provider** (e.g. `XFormProvider`) receives ids and optional callbacks (e.g. `disabled`).
- It calls the data hook; if `isLoading` it renders a spinner; otherwise it renders the **form component** with `defaultValues` and any entity data (e.g. for title).
- The provider does **not** render `Drawer.*`; it does not receive `onClose` or drawer props. It only prepares data and renders the form.

### 3. Form owns drawer UI and close

- The **form component** (the one that will be the drawer content) uses `useDrawerLayer()` and calls `closeDrawer()` when the user cancels or submits successfully.
- It renders the drawer chrome with **Composition**: `Drawer.Header` (e.g. title, `onClose={closeDrawer}`), `Drawer.Body` (form fields), `Drawer.Actions` (primary/secondary with loading from form state, e.g. `isSubmitting`).
- So: the form holds `useForm`, submit logic, and drawer chrome; the opener never passes `onClose` or button props.

### 4. Form pure + wrapper (optional but recommended)

- Split the form into a **pure form** (only fields + validation + submit logic, no `Drawer.*`) and a **wrapper** that provides `useForm`, Drawer.Header/Body/Actions, and the pure form as content.
- This allows reusing the same form in a full page or another layout without the drawer.

### 5. Opener uses useDrawerLayer and passes content

- In the component that used to call `useDrawer` and render a drawer node:
  - Remove `useDrawer` and any drawer node from JSX.
  - Use `useDrawerLayer()` and call `openDrawer({ content: <XFormProvider ... />, wrapperProps })` with the right props (ids, disabled, etc.) and optional `wrapperProps` (e.g. `PaperProps.sx` for width).
- Do **not** pass `onClose`, `onSuccess`, or similar to the provider/form; the form closes itself with `closeDrawer()`.

### 6. Optional: encapsulate open in a hook

- If the same drawer is opened from several places (e.g. list menu + row action), consider a small **open hook** (e.g. `useXDrawer(ids, disabled)`) that returns `openDrawer` (or `openDrawer(type, id)` if you have several variants).
- Each opener (list, card menu, etc.) calls this hook and uses the returned function; no need to pass `openDrawer` or `onEdit` down as props if the place that has the action (e.g. menu) can call the hook itself.

### 7. Types and docs

- Type the data hook result and provider props clearly (no `onClose` in the public API when using Composition).
- Add a short comment where the drawer is opened (or in the module README) that it uses DrawerLayer and that the form content owns the drawer UI and close behavior.

---

## Checklist before starting

- [ ] Confirm the drawer is a good fit for **Composition + useDrawerLayer** (content owns state; opener does not need to update config reactively).
- [ ] Locate current opener, wrapper, and form; list what data is loaded and where.
- [ ] Ensure the app is already wrapped in `DrawerLayerProvider` (e.g. in theme/root).

---

## Checklist after migration

- [ ] No `useDrawer` (or legacy drawer hook) in the opener; no drawer node in JSX.
- [ ] Opener only calls `openDrawer({ content: <... />, wrapperProps })` (or a custom hook that does that).
- [ ] Form (or wrapper) uses `useDrawerLayer().closeDrawer()` for close; no `onClose`/`onSuccess` passed from opener.
- [ ] Drawer chrome (header, body, actions) is rendered inside the form/wrapper with `Drawer.Header`, `Drawer.Body`, `Drawer.Actions`.
- [ ] Types and README/comments updated.

---

## Example flow (from REFINEMENT)

1. **Data:** `useGetFormDefaultValues(processId, actionId, getDefaultValuesFn)` → defaultValues, isLoading, error; use `useQuery(getProcessAction)` when the form needs the action (e.g. title).
2. **Provider:** `CustomTaskFormProvider` / `SurveyConfigFormProvider` call that hook; if loading → spinner; else render form with defaultValues and action.
3. **Form:** CustomTaskForm / SurveyConfigForm use `closeDrawer()` from `useDrawerLayer()` and render Drawer.Header + Body + Actions; form pure in `CustomTaskFormContent` / `SurveyConfigFormContent`.
4. **Opener:** StepActionList and ProcessActionActions use `useActionDrawer({ processId, stepId, disabled })` and call `openDrawer(type, actionId)` (menu “new” and “edit”); no `onEdit` or `openDrawer` passed to the card.
5. **Removed:** ConnectedStepAction and any `useDrawer` usage; no drawer nodes in list JSX.

---

## References

- **REFINEMENT (full plan and rationale):** `.refinements/REFINEMENT-StepActionList-DrawerLayer.md`
- **material-hu DrawerLayer:** `material-hu/src/components/layers/Drawers/` (README, hooks, types)
- **Backoffice provider:** `DrawerLayerProvider` in `useHuGoTheme.tsx`
