bug(mock-doc): `Element.toggleAttribute()` is missing, so components that use it fail during SSR

Abierto Apto para principiantes
#7,015 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
78/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
typescript
Área
web-dev

Línea de trabajo

El método pertenece a MockElement en packages/mock-doc/src/node.ts, junto a hasAttribute, setAttribute y removeAttribute. Lee primero esos para seguir sus convenciones, y luego reproduce el método que falta llamando a toggleAttribute sobre un elemento de MockDocument. Se considera terminado cuando toggleAttribute cubre los cuatro casos de la tabla del issue (con y sin force) y devuelve el booleano correcto, y las pruebas existentes de mock-doc siguen pasando.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

triage
Prerequisites
Stencil Version

4.45.3 and 5.0.0-rc.1

Current Behavior

mock-doc's MockElement doesn't implement Element.toggleAttribute(). Server rendering runs components on mock-doc, so a component that calls it works in the browser but throws during renderToString(). The render still completes, but the attribute is missing from the output and the error appears only in diagnostics, which makes the bug easy to miss.

toggleAttribute() is the standard way to set a boolean attribute from state, and every browser Stencil 5 supports has it, so component authors reach for it naturally.

Expected Behavior
Workaround

Authors replace it with an explicit branch:

active ? this.el.setAttribute('data-active', '') : this.el.removeAttribute('data-active');

It works, but every component that may be server-rendered has to know to avoid a standard DOM method.

Suggested fix

Add toggleAttribute() to MockElement in packages/mock-doc/src/node.ts, next to hasAttribute, setAttribute and removeAttribute, with the DOM's semantics for the optional force argument:

toggleAttribute(attrName: string, force?: boolean): boolean {
  const has = this.hasAttribute(attrName);
  if (force === undefined ? !has : force) {
    if (!has) this.setAttribute(attrName, '');
    return true;
  }
  if (has) this.removeAttribute(attrName);
  return false;
}
┌─────────────────────────────┬──────────────────┬───────────┬─────────┐
│            Call             │ Attribute before │   After   │ Returns │
├─────────────────────────────┼──────────────────┼───────────┼─────────┤
│ toggleAttribute('a')        │ absent           │ a=""      │ true    │
├─────────────────────────────┼──────────────────┼───────────┼─────────┤
│ toggleAttribute('a')        │ present          │ absent    │ false   │
├─────────────────────────────┼──────────────────┼───────────┼─────────┤
│ toggleAttribute('a', true)  │ present          │ unchanged │ true    │
├─────────────────────────────┼──────────────────┼───────────┼─────────┤
│ toggleAttribute('a', false) │ absent           │ unchanged │ false   │
└─────────────────────────────┴──────────────────┴───────────┴─────────┘

Going through setAttribute and removeAttribute keeps mock-doc's existing attame lowercasing, and the attributeChangedCallback behavior.

System Info

Steps to Reproduce

Reproduction

import { Component, Element, Prop, h } from '@stencil/core';

@Component({ tag: 'my-flag', encapsulation: { type: 'shadow' } })
export class MyFlag {
  @Element() el!: HTMLElement;
  @Prop() active = false;

  componentWillLoad() {
    this.el.toggleAttribute('data-active', this.active);
  }

  render() {
    return <slot />;
  }
}

import { renderToString } from './hydrate/index.js';

const r = await renderToString('<my-flag active>Hi</my-flag>', { fullDocument: false });
console.log(r.html);        // <my-flag active="" class="hydrated" s-id="1">…: no data-active
console.log(r.diagnostics); // TypeError: this.el.toggleAttribute is not a function

The same happens with new MockDocument() directly: typeof doc.createElement('div').toggleAttribute is 'undefined', on 4.45.3 and 5.0.0-rc.1.

### Code Reproduction URL

https://github.com

### Additional Information

_No response_
Lenguaje dominante
TypeScript
Estrellas
13.2k
Forks
865
Merge medio
2 d 16 h
PR fusionados (30 d)
29

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de stenciljs/core

Todos los issues de stenciljs/core

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.