> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui-web/migration.md).

# Guía de migración

Esta guía cubre el proceso de migrar componentes de UI1 (`decentraland-ui`) a UI2 (`decentraland-ui2`).

## Cuándo migrar

Las migraciones pueden comenzar por tres razones válidas:

### 1. Mejora técnica

Quieres migrar un componente por:

* Mejor compatibilidad con temas
* Tipos de TypeScript mejorados
* Coherencia con otros componentes de UI2
* Optimización del rendimiento
* Mejoras de accesibilidad

### 2. Actualización de un componente

* Un componente de UI1 necesita ser actualizado
* Todavía no existe un equivalente en UI2
* **Requisito**: Créalo primero en UI2 y luego úsalo

### 3. Necesidades del proyecto

* El componente se utilizará en un proyecto nuevo o existente
* Hay tiempo disponible para migrarlo correctamente
* Los recursos del proyecto permiten una migración exhaustiva

{% hint style="warning" %}
**No** migres componentes "solo porque sí". Cada migración debe tener una justificación empresarial o técnica clara.
{% endhint %}

***

## Proceso de migración

### Paso 1: Planificación

Antes de comenzar la migración:

1. **Identifica dependencias**
   * ¿Qué otros componentes utiliza?
   * ¿Qué proyectos lo usan actualmente?
   * ¿Hay algún breaking change planificado?
2. **Revisa el uso actual**
   * ¿Cuántos proyectos usan este componente?
   * ¿Qué props se usan con más frecuencia?
   * ¿Hay problemas conocidos?
3. **Define el alcance**
   * ¿Será una migración 1:1?
   * ¿Hay mejoras planificadas?
   * ¿Cuál es el cronograma?

### Paso 2: Crear el componente de UI2

Sigue la [Componentes personalizados](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui-web/custom-components.md) guía para componentes candidatos de UI2.

**Requisitos:**

* Usa sintaxis de objeto para styled-components
* Usa solo valores del tema (sin valores arbitrarios)
* Añade historias completas de Storybook
* Escribe pruebas completas
* Documenta todas las props y comportamientos

**Estructura de ejemplo:**

```
ui2/src/components/Button/
├── Button.tsx
├── Button.styles.ts
├── Button.stories.tsx
├── Button.test.tsx
├── types.ts
├── index.ts
└── README.md
```

### Paso 3: Mantener la compatibilidad

El componente de UI2 **DEBE** debe exponer las mismas props y comportamientos que la versión de UI1.

#### Mismas props

```tsx
// Button de UI1
interface ButtonProps {
  primary?: boolean;
  size?: 'small' | 'medium' | 'large';
  onClick?: () => void;
  disabled?: boolean;
  children: React.ReactNode;
}

// Button de UI2 - DEBE admitir las mismas props
interface ButtonProps {
  primary?: boolean;
  size?: 'small' | 'medium' | 'large';
  onClick?: () => void;
  disabled?: boolean;
  children: React.ReactNode;
  // Se pueden añadir nuevas props opcionales
  variant?: 'text' | 'outlined' | 'contained';
}
```

#### Cambios compatibles con versiones anteriores

Si necesitas cambiar o añadir props:

1. **Primero**, añade las nuevas props a UI1 como opcionales
2. **Después**, migra a los consumidores de UI1 para que usen las nuevas props
3. **Por último**, crea el componente de UI2 con la nueva API

```tsx
// Paso 1: Añadir prop opcional a UI1
interface ButtonProps {
  primary?: boolean;
  // Nueva prop opcional
  variant?: 'primary' | 'secondary';
}

// Paso 2: Actualizar la implementación de UI1
export function Button({ primary, variant = primary ? 'primary' : 'secondary' }: ButtonProps) {
  // Usa variant en lugar de primary internamente
}

// Paso 3: Crear UI2 con la nueva API
interface ButtonProps {
  // variant ahora es la prop principal
  variant?: 'primary' | 'secondary';
  // Mantén primary por compatibilidad, márcala como obsoleta
  /** @deprecated Usa variant en su lugar */
  primary?: boolean;
}
```

### Paso 4: Marcar como obsoleto el componente de UI1

Añade un aviso de deprecación al componente de UI1:

````tsx
/**
 * @deprecated Este componente se ha migrado a UI2.
 * Importa desde 'decentraland-ui2' en su lugar:
 * 
 * ```tsx
 * import { Button } from 'decentraland-ui2';
 * ```
 * 
 * Consulta la guía de migración: https://docs.decentraland.org/contributor-guides/web-ui-standards/migration
 */
export function Button(props: ButtonProps) {
  // ... implementación existente
}
````

### Paso 5: Adopción gradual

No fuerces una migración inmediata. Permite una adopción gradual:

1. **Publica** componente de UI2
2. **Documenta** ruta de migración
3. **Actualiza** los nuevos proyectos para que usen UI2
4. **Migra** los proyectos existentes de forma oportunista
5. **Planifica** la eventual eliminación de UI1 (con aviso)

***

## Ejemplos de migración

### Ejemplo 1: Componente simple

Migración de un `Card` componente:

#### Versión de UI1

```tsx
// decentraland-ui/src/components/Card/Card.tsx
import React from 'react';
import './Card.css';

export interface CardProps {
  className?: string;
  children: React.ReactNode;
}

export function Card({ className, children }: CardProps) {
  return (
    <div className={`dcl-card ${className || ''}`}>
      {children}
    </div>
  );
}
```

#### Versión de UI2

```tsx
// decentraland-ui2/src/components/Card/Card.tsx
import { styled } from '@mui/material/styles';

export interface CardProps {
  className?: string;
  children: React.ReactNode;
}

const StyledCard = styled('div')(({ theme }) => ({
  backgroundColor: theme.palette.background.paper,
  borderRadius: theme.shape.borderRadius,
  padding: theme.spacing(2),
  boxShadow: theme.shadows[1],
  
  [theme.breakpoints.down('sm')]: {
    padding: theme.spacing(1),
  },
}));

export function Card({ className, children }: CardProps) {
  return (
    <StyledCard className={className}>
      {children}
    </StyledCard>
  );
}
```

### Ejemplo 2: Componente con variantes

Migración de un `Button` con variantes:

#### Versión de UI1

```tsx
// UI1
import './Button.css';

interface ButtonProps {
  primary?: boolean;
  secondary?: boolean;
  size?: 'small' | 'medium' | 'large';
}

export function Button({ primary, secondary, size = 'medium', ...props }: ButtonProps) {
  const classes = [
    'dcl-button',
    primary && 'primary',
    secondary && 'secondary',
    `size-${size}`,
  ].filter(Boolean).join(' ');
  
  return <button className={classes} {...props} />;
}
```

#### Versión de UI2

```tsx
// UI2
import { styled } from '@mui/material/styles';

interface ButtonProps {
  /** @deprecated Usa variant="contained" en su lugar */
  primary?: boolean;
  /** @deprecated Usa variant="outlined" en su lugar */
  secondary?: boolean;
  variant?: 'text' | 'outlined' | 'contained';
  size?: 'small' | 'medium' | 'large';
}

const StyledButton = styled('button')<ButtonProps>(({ theme, variant = 'contained', size = 'medium' }) => {
  const sizes = {
    small: theme.spacing(0.5, 1),
    medium: theme.spacing(1, 2),
    large: theme.spacing(1.5, 3),
  };
  
  const variants = {
    text: {
      backgroundColor: 'transparent',
      color: theme.palette.primary.main,
    },
    outlined: {
      backgroundColor: 'transparent',
      color: theme.palette.primary.main,
      border: `1px solid ${theme.palette.primary.main}`,
    },
    contained: {
      backgroundColor: theme.palette.primary.main,
      color: theme.palette.primary.contrastText,
    },
  };
  
  return {
    padding: sizes[size],
    borderRadius: theme.shape.borderRadius,
    border: 'none',
    cursor: 'pointer',
    ...variants[variant],
    
    '&:hover': {
      opacity: 0.9,
    },
    
    '&:disabled': {
      opacity: 0.5,
      cursor: 'not-allowed',
    },
  };
});

export function Button({ 
  primary, 
  secondary, 
  variant, 
  ...props 
}: ButtonProps) {
  // Gestiona las props obsoletas
  const actualVariant = variant || 
    (primary ? 'contained' : secondary ? 'outlined' : 'text');
  
  return <StyledButton variant={actualVariant} {...props} />;
}
```

***

## Lista de verificación de migración

Usa esta lista de verificación para cada migración de componente:

### Fase de planificación

* [ ] Identifica todos los proyectos que usan el componente
* [ ] Documenta las props y comportamientos actuales
* [ ] Define el alcance y el cronograma de la migración
* [ ] Obtén la aprobación de las partes interesadas

### Fase de implementación

* [ ] Crea el componente de UI2 siguiendo los estándares
* [ ] Mantén la compatibilidad de las props
* [ ] Usa sintaxis de objeto para el estilo
* [ ] Usa solo valores del tema
* [ ] Implementa todos los estados (inactivo, hover, focus, deshabilitado, error)
* [ ] Añade historias completas de Storybook
* [ ] Escribe pruebas unitarias
* [ ] Documenta las funciones de accesibilidad

### Fase de deprecación

* [ ] Añade un aviso de deprecación al componente de UI1
* [ ] Actualiza la documentación de UI1
* [ ] Crea una guía de migración para los consumidores
* [ ] Publica el componente de UI2

### Fase de adopción

* [ ] Actualiza los nuevos proyectos para que usen UI2
* [ ] Crea PRs de migración para los proyectos existentes
* [ ] Supervisa si hay problemas
* [ ] Recopila comentarios
* [ ] Planifica el cronograma de eliminación de UI1

***

## Patrones comunes de migración

### De CSS a Styled Components

```tsx
// UI1: archivo CSS
.dcl-card {
  background: #fff;
  padding: 16px;
  border-radius: 8px;
}

// UI2: componente estilizado
const Card = styled('div')(({ theme }) => ({
  backgroundColor: theme.palette.background.paper,
  padding: theme.spacing(2),
  borderRadius: theme.shape.borderRadius,
}));
```

### De nombres de clase a props

```tsx
// UI1: variantes basadas en clases
<Button className={primary ? 'primary' : 'secondary'} />

// UI2: variantes basadas en props
<Button variant={primary ? 'contained' : 'outlined'} />
```

### De valores fijos al tema

```tsx
// UI1: valores fijos
const styles = {
  color: '#333',
  fontSize: '14px',
  padding: '8px 16px',
};

// UI2: valores del tema
const Component = styled('div')(({ theme }) => ({
  color: theme.palette.text.primary,
  fontSize: theme.typography.body2.fontSize,
  padding: theme.spacing(1, 2),
}));
```

***

## Cambios incompatibles

A veces son necesarios cambios incompatibles. Manéjalos con cuidado:

### Cuándo son aceptables los cambios incompatibles

* Correcciones de seguridad
* Errores críticos
* Actualizaciones de versión mayor
* Eliminación de funciones obsoletas (con aviso)

### Cómo manejar los cambios incompatibles

1. **Anúncialo con antelación** - Comunica los cambios con suficiente antelación
2. **Proporciona una ruta de migración** - Documenta cómo actualizar
3. **Incremento de versión** - Sigue el versionado semántico
4. **Período de deprecación** - Da tiempo para migrar
5. **Codemods** - Proporciona herramientas automáticas de migración si es posible

### Ejemplo: Eliminación de props obsoletas

```tsx
// Versión 1.0: Introduce la nueva API, marca la anterior como obsoleta
interface ButtonProps {
  /** @deprecated Usa variant="contained" en su lugar */
  primary?: boolean;
  variant?: 'text' | 'outlined' | 'contained';
}

// Versión 1.5: Advierte sobre la eliminación
interface ButtonProps {
  /** @deprecated Se eliminará en 2.0. Usa variant en su lugar */
  primary?: boolean;
  variant?: 'text' | 'outlined' | 'contained';
}

// Versión 2.0: Elimina la prop obsoleta
interface ButtonProps {
  variant?: 'text' | 'outlined' | 'contained';
}
```

***

## Pruebas de migración

Asegúrate de que los componentes migrados funcionen correctamente:

### Pruebas de regresión visual

Compara visualmente los componentes de UI1 y UI2:

```tsx
// Historia de Storybook para comparación
export const ComparisonStory: Story = {
  render: () => (
    <div style={{ display: 'flex', gap: '2rem' }}>
      <div>
        <h3>UI1</h3>
        <UI1Button primary>Click Me</UI1Button>
      </div>
      <div>
        <h3>UI2</h3>
        <UI2Button variant="contained">Click Me</UI2Button>
      </div>
    </div>
  ),
};
```

### Pruebas de comportamiento

Asegúrate de que las props funcionen igual:

```tsx
describe('Button migration', () => {
  it('debería gestionar la prop primary (obsoleta) igual que variant="contained"', () => {
    const { container: ui1 } = render(<UI1Button primary>Test</UI1Button>);
    const { container: ui2 } = render(<UI2Button primary>Test</UI2Button>);
    
    // Compara la salida renderizada
    expect(ui1.textContent).toBe(ui2.textContent);
  });
});
```

***

## Actualizaciones de documentación

Después de la migración, actualiza la documentación:

### Actualiza la documentación del componente de UI1

```markdown
# Button (UI1 - Obsoleto)

> ⚠️ **Este componente se ha migrado a UI2.**
> Consulta la [documentación de Button de UI2](../ui2/button) para la nueva versión.

Este componente está obsoleto y se eliminará en una versión futura.
Por favor, migra a UI2.

## Guía de migración

Consulta [Guía de migración](./migration) para más detalles.
```

### Crea la documentación del componente de UI2

```markdown
# Button (UI2)

Componente moderno de Button con compatibilidad total con el tema.

## Migración desde UI1

Si estás migrando desde UI1:

- `primary` prop → `variant="contained"`
- `secondary` prop → `variant="outlined"`
- Clases CSS → styled-components

Consulta la [guía de migración completa](./migration) para más detalles.
```

***

## Próximos pasos

* Revisar [Componentes personalizados](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui-web/custom-components.md) para crear componentes de UI2
* Ver [Estilo y temas](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui-web/styling-and-theming.md) para estándares de estilo
* Revisa [Resumen del proceso](/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui-web/process-overview.md) para el flujo de trabajo completo


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/contributor/contributor-es/guias-para-colaboradores/estandares-de-ui-web/migration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
