"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Aprender TDD haciendo: etiquetar miembros en el editor de texto enriquecido de Umbraco

Aprender TDD haciendo: etiquetar miembros en el editor de texto enriquecido de Umbraco

Publicado el 2024-11-02
Navegar:255

Learning TDD by doing: Tagging members in Umbraco

En el sistema que estoy creando, necesito poder mencionar a los miembros de Umbraco en texto en el sitio web. Para hacer eso, necesito crear una extensión para el editor de texto enriquecido de Umbraco: TinyMCE.

Contexto

Como editor de contenido, quiero etiquetar a los miembros en un mensaje o artículo para que reciban notificaciones sobre contenido nuevo sobre ellos.

Miré implementaciones similares, como en Slack o en X. Slack usa una etiqueta html especial para las menciones durante la escritura, pero luego envía los datos al backend con un token con un formato específico. Decidí adoptar un enfoque similar, pero por ahora olvídate del paso de traducción. En el contenido, una mención se verá así:


@D_Inventor

Exploración inicial

Antes de comenzar a construir, estaba buscando formas de conectarme a TinyMCE en Umbraco. Esta es una de las cosas que menos me gusta ampliar en el backoffice de Umbraco. Sin embargo, ya hice esto antes y me resultó más fácil ampliar el editor si creo un decorador en tinyMceService de Umbraco en AngularJS. En la documentación de TinyMCE, encontré una función llamada 'autoCompleters', que hacía exactamente lo que necesitaba, así que ahí estaba mi enlace al editor. Mi código inicial (sin ninguna prueba todavía) tenía este aspecto:


rtedecorator.$inject = ["$delegate"];
export function rtedecorator($delegate: any) {
  const original = $delegate.initializeEditor;

  $delegate.initializeEditor = function (args: any) {
    original.apply($delegate, arguments);

    args.editor.contentStyles.push("mention { background-color: #f7f3c1; }");
    args.editor.ui.registry.addAutocompleter("mentions", {
      trigger: "@",
      fetch: (
        pattern: string,
        maxResults: number,
        _fetchOptions: Record
      ): Promise
        // TODO: fetch from backend
        => Promise.resolve([{ type: "autocompleteitem", value: "1234", text: "D_Inventor" }]),
      onAction: (api: any, rng: Range, value: string): void => {
        // TODO: business logic
        api.hide();
      },
    });
  };

  return $delegate;
}


Estoy usando vite y mecanografiado en este proyecto, pero no tengo ningún tipo para TinyMCE instalado. Por ahora me quedaré con any y trataré de evitar TinyMCE tanto como sea posible.

Construyendo con TDD

Decidí usar Jest para realizar pruebas. Encontré un comienzo fácil y rápidamente logré que algo funcionara.

✅ Éxito
Aprendí una nueva herramienta para pruebas unitarias en código frontend. Apliqué con éxito la herramienta para escribir un frontend con pruebas unitarias

Escribí mi primera prueba:

mencionar-manager.test.ts


describe("MentionsManager.fetch", () => {
  let sut: MentionsManager;
  let items: IMention[];

  beforeEach(() => {
    items = [];
    sut = new MentionsManager();
  });

  test("should be able to fetch one result", async () => {
    items.push({ userId: "1234", userName: "D_Inventor" });
    const result = await sut.fetch(1);
    expect(result).toHaveLength(1);
  });
});


Me sorprendió un poco el rigor del compilador mecanografiado. Trabajar en pasos aquí realmente significó no agregar nada que no estés usando todavía. Por ejemplo, quería agregar una referencia a la "UI", porque sabía que la iba a usar más adelante, pero en realidad no podía compilar MentionsManager hasta que usara todo lo que puse en el constructor.

Después de algunas rondas de rojo, verde y refactorización, terminé con estas pruebas:

mencionar-manager.test.ts


describe("MentionsManager.fetch", () => {
  let sut: MentionsManager;
  let items: IMention[];

  beforeEach(() => {
    items = [];
    sut = new MentionsManager(() => Promise.resolve(items));
  });

  test("should be able to fetch one result", async () => {
    items.push({ userId: "1234", userName: "D_Inventor" });
    const result = await sut.fetch(1);
    expect(result).toHaveLength(1);
  });

  test("should be able to fetch empty result", async () => {
    const result = await sut.fetch(1);
    expect(result).toHaveLength(0);
  });

  test("should be able to fetch many results", async () => {
    items.push({ userId: "1324", userName: "D_Inventor" }, { userId: "3456", userName: "D_Inventor2" });
    const result = await sut.fetch(2);
    expect(result).toHaveLength(2);
  });

  test("should return empty list upon error", () => {
    const sut = new MentionsManager(() => {
      throw new Error("Something went wrong while fetching");
    }, {} as IMentionsUI);
    return expect(sut.fetch(1)).resolves.toHaveLength(0);
  });
});


Con esta lógica implementada, podría recuperar menciones de cualquier fuente y mostrarlas en el RTE a través del enlace "buscar".
Utilicé el mismo enfoque para crear un método de "selección" para tomar al miembro seleccionado e insertar la mención en el editor. Este es el código con el que terminé:

mencionar-manager.ts


export class MentionsManager {
  private mentions: IMention[] = [];

  constructor(
    private source: MentionsAPI,
    private ui: IMentionsUI
  ) {}

  async fetch(take: number, query?: string): Promise {
    try {
      const result = await this.source(take, query);
      if (result.length === 0) return [];
      this.mentions = result;

      return result;
    } catch {
      return [];
    }
  }

  pick(id: string, location: Range): void {
    const mention = this.mentions.find((m) => m.userId === id);
    if (!mention) return;

    this.ui.insertMention(mention, location);
  }
}


❓ Incertidumbre
La interfaz Range es un tipo integrado que es realmente difícil de burlar y esta interfaz filtra un detalle de implementación en mi lógica de negocios. Siento que podría haber habido una mejor manera de hacer esto.

Retrospección

En general, creo que terminé con un código simple que es fácil de cambiar. Todavía hay partes de este código que realmente no me gustan. Quería que la lógica empresarial controlara la interfaz de usuario, pero el código terminó más bien como una tienda simple que también realiza una única llamada a la interfaz de usuario. Me pregunto si podría ajustar mejor la interfaz de usuario para aprovechar más el administrador.

Declaración de liberación Este artículo se reproduce en: https://dev.to/d_inventor/learning-tdd-by-doing-tagging-members-in-umbracos-rich-text-editor-29o4?1 Si hay alguna infracción, comuníquese con Study_golang @163.com eliminar
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3