> 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-pt/guias-para-colaboradores/padroes-de-web-ui/migration.md).

# Guia de migração

Este guia cobre o processo de migração de componentes de UI1 (`decentraland-ui`) para UI2 (`decentraland-ui2`).

## Quando Migrar

As migrações podem começar por três razões válidas:

### 1. Melhoria Técnica

Quer migrar um componente por:

* Melhor suporte a temas
* Tipos TypeScript melhorados
* Consistência com outros componentes UI2
* Otimização de desempenho
* Melhorias de acessibilidade

### 2. Atualização de um Componente

* É necessário atualizar um componente UI1
* Ainda não existe um equivalente em UI2
* **Requisito**: Crie-o primeiro em UI2 e depois utilize-o

### 3. Necessidades do Projeto

* O componente será usado num projeto novo ou existente
* Há tempo disponível para o migrar corretamente
* Os recursos do projeto permitem uma migração completa

{% hint style="warning" %}
**Não** migre componentes "só porque sim". Cada migração deve ter uma justificação de negócio ou técnica clara.
{% endhint %}

***

## Processo de Migração

### Passo 1: Planeamento

Antes de iniciar a migração:

1. **Identifique dependências**
   * Que outros componentes ele usa?
   * Que projetos o usam atualmente?
   * Há alguma breaking change planeada?
2. **Reveja o uso atual**
   * Quantos projetos usam este componente?
   * Quais props são mais usadas?
   * Há algum problema conhecido?
3. **Defina o âmbito**
   * Será esta uma migração 1:1?
   * Há melhorias planeadas?
   * Qual é o cronograma?

### Passo 2: Criar o componente UI2

Siga o [Componentes personalizados](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-web-ui/custom-components.md) guia para componentes candidatos a UI2.

**Requisitos:**

* Use sintaxe de objeto para styled-components
* Use apenas valores do tema (sem valores arbitrários)
* Adicione stories abrangentes no Storybook
* Escreva testes completos
* Documente todas as props e comportamentos

**Estrutura de exemplo:**

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

### Passo 3: Manter a Compatibilidade

O componente UI2 **DEVE** deve expor as mesmas props e comportamentos da versão UI1.

#### Mesmas Props

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

// UI2 Button - DEVE suportar as mesmas props
interface ButtonProps {
  primary?: boolean;
  size?: 'small' | 'medium' | 'large';
  onClick?: () => void;
  disabled?: boolean;
  children: React.ReactNode;
  // Pode adicionar novas props opcionais
  variant?: 'text' | 'outlined' | 'contained';
}
```

#### Alterações Compatíveis com Versões Anteriores

Se precisar alterar ou adicionar props:

1. **Primeiro**, adicione as novas props ao UI1 como opcionais
2. **Depois**, migre os consumidores de UI1 para usar as novas props
3. **Por fim**, crie o componente UI2 com a nova API

```tsx
// Passo 1: Adicionar prop opcional ao UI1
interface ButtonProps {
  primary?: boolean;
  // Nova prop opcional
  variant?: 'primary' | 'secondary';
}

// Passo 2: Atualizar a implementação do UI1
export function Button({ primary, variant = primary ? 'primary' : 'secondary' }: ButtonProps) {
  // Usar variant em vez de primary internamente
}

// Passo 3: Criar UI2 com a nova API
interface ButtonProps {
  // variant é agora a prop principal
  variant?: 'primary' | 'secondary';
  // Manter primary para compatibilidade, marcar como descontinuada
  /** @deprecated Use variant instead */
  primary?: boolean;
}
```

### Passo 4: Descontinuar o Componente UI1

Adicione um aviso de descontinuação ao componente UI1:

````tsx
/**
 * @deprecated Este componente foi migrado para UI2.
 * Importe de 'decentraland-ui2' em vez disso:
 * 
 * ```tsx
 * import { Button } from 'decentraland-ui2';
 * ```
 * 
 * Consulte o guia de migração: https://docs.decentraland.org/contributor-guides/web-ui-standards/migration
 */
export function Button(props: ButtonProps) {
  // ... implementação existente
}
````

### Passo 5: Adoção Gradual

Não force a migração imediata. Permita a adoção gradual:

1. **Publicar** componente UI2
2. **Documentar** o caminho de migração
3. **Atualizar** novos projetos para usar UI2
4. **Migrar** projetos existentes à medida que surgirem oportunidades
5. **Planear** a remoção eventual de UI1 (com aviso)

***

## Exemplos de Migração

### Exemplo 1: Componente Simples

A migrar um componente básico `Card` componente:

#### Versão 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>
  );
}
```

#### Versão 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>
  );
}
```

### Exemplo 2: Componente com Variantes

A migrar um `Button` com variantes:

#### Versão 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} />;
}
```

#### Versão UI2

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

interface ButtonProps {
  /** @deprecated Use variant="contained" instead */
  primary?: boolean;
  /** @deprecated Use variant="outlined" instead */
  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) {
  // Tratar props descontinuadas
  const actualVariant = variant || 
    (primary ? 'contained' : secondary ? 'outlined' : 'text');
  
  return <StyledButton variant={actualVariant} {...props} />;
}
```

***

## Lista de Verificação de Migração

Use esta lista de verificação para cada migração de componente:

### Fase de Planeamento

* [ ] Identifique todos os projetos que usam o componente
* [ ] Documente as props e comportamentos atuais
* [ ] Defina o âmbito e o cronograma da migração
* [ ] Obtenha aprovação das partes interessadas

### Fase de Implementação

* [ ] Crie o componente UI2 seguindo os padrões
* [ ] Mantenha a compatibilidade das props
* [ ] Use sintaxe de objeto para o estilo
* [ ] Use apenas valores do tema
* [ ] Implemente todos os estados (idle, hover, focus, disabled, error)
* [ ] Adicione stories abrangentes no Storybook
* [ ] Escreva testes unitários
* [ ] Documente as funcionalidades de acessibilidade

### Fase de Descontinuação

* [ ] Adicione um aviso de descontinuação ao componente UI1
* [ ] Atualize a documentação do UI1
* [ ] Crie um guia de migração para os consumidores
* [ ] Publique o componente UI2

### Fase de Adoção

* [ ] Atualize novos projetos para usar UI2
* [ ] Crie PRs de migração para projetos existentes
* [ ] Monitorize problemas
* [ ] Recolha feedback
* [ ] Planeie o cronograma de remoção de UI1

***

## Padrões Comuns de Migração

### De CSS para Styled Components

```tsx
// UI1: ficheiro 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 Nomes de Classes para Props

```tsx
// UI1: variantes baseadas em classes
<Button className={primary ? 'primary' : 'secondary'} />

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

### De Valores Fixos para Tema

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

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

***

## Alterações Incompatíveis

Por vezes, são necessárias alterações incompatíveis. Trate-as com cuidado:

### Quando as Alterações Incompatíveis São Aceitáveis

* Correções de segurança
* Erros críticos
* Atualizações de versão principal
* Remoção de funcionalidades descontinuadas (com aviso)

### Como Tratar as Alterações Incompatíveis

1. **Anuncie antecipadamente** - Comunique as alterações com bastante antecedência
2. **Forneça um caminho de migração** - Documente como atualizar
3. **Aumento de versão** - Siga o versionamento semântico
4. **Período de descontinuação** - Dê tempo para migrar
5. **Codemods** - Forneça ferramentas automáticas de migração, se possível

### Exemplo: Remoção de Props Descontinuadas

```tsx
// Versão 1.0: Introduzir a nova API, descontinuar a antiga
interface ButtonProps {
  /** @deprecated Use variant="contained" instead */
  primary?: boolean;
  variant?: 'text' | 'outlined' | 'contained';
}

// Versão 1.5: Avisar sobre a remoção
interface ButtonProps {
  /** @deprecated Será removido na versão 2.0. Use variant instead */
  primary?: boolean;
  variant?: 'text' | 'outlined' | 'contained';
}

// Versão 2.0: Remover a prop descontinuada
interface ButtonProps {
  variant?: 'text' | 'outlined' | 'contained';
}
```

***

## Testar a Migração

Garanta que os componentes migrados funcionam corretamente:

### Testes de Regressão Visual

Compare visualmente os componentes UI1 e UI2:

```tsx
// Story do Storybook para comparação
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>
  ),
};
```

### Testes Comportamentais

Garanta que as props funcionam da mesma forma:

```tsx
describe('Migração do Button', () => {
  it('deve tratar a prop primary (obsoleta) da mesma forma que variant="contained"', () => {
    const { container: ui1 } = render(<UI1Button primary>Test</UI1Button>);
    const { container: ui2 } = render(<UI2Button primary>Test</UI2Button>);
    
    // Compare o output renderizado
    expect(ui1.textContent).toBe(ui2.textContent);
  });
});
```

***

## Atualizações da Documentação

Depois da migração, atualize a documentação:

### Atualize a documentação do componente UI1

```markdown
# Button (UI1 - Descontinuado)

> ⚠️ **Este componente foi migrado para UI2.**
> Consulte a [documentação do UI2 Button](../ui2/button) para a nova versão.

Este componente está descontinuado e será removido numa versão futura.
Migre para UI2.

## Guia de Migração

Consulte o [Guia de Migração](./migration) para detalhes.
```

### Crie a documentação do componente UI2

```markdown
# Button (UI2)

Componente moderno de botão com suporte total ao tema.

## Migração a partir de UI1

Se estiver a migrar a partir de UI1:

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

Consulte o [Guia de Migração](./migration) completo para detalhes.
```

***

## Próximos passos

* Rever [Componentes personalizados](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-web-ui/custom-components.md) para criar componentes UI2
* Veja [Estilização e Temas](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-web-ui/styling-and-theming.md) para padrões de estilização
* Verifique [Visão geral do processo](/contributor/contributor-pt/guias-para-colaboradores/padroes-de-web-ui/process-overview.md) para o fluxo de trabalho 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-pt/guias-para-colaboradores/padroes-de-web-ui/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.
