」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 透過實作學習 TDD:在 Umbraco 的富文本編輯器中標記成員

透過實作學習 TDD:在 Umbraco 的富文本編輯器中標記成員

發佈於2024-11-02
瀏覽:951

Learning TDD by doing: Tagging members in Umbraco

在我正在建構的系統中,我需要能夠在網站的文字中提及 Umbraco 會員。為此,我需要建立 Umbraco 富文本編輯器的擴充:TinyMCE。

情境

作為內容編輯者,我想在訊息或文章中標記成員,以便他們收到有關其新內容的通知。

我研究了類似的實現,例如 Slack 或 X 上的實現。 Slack 在編寫過程中使用特殊的 html 標籤進行提及,然後使用特定格式的令牌將資料傳送到後端。我決定採取類似的方法,但現在忘記翻譯步驟。在內容中,提及將如下所示:


@D_Inventor


初步探索

在開始建置之前,我一直在尋找連接 Umbraco 中的 TinyMCE 的方法。這是我最不喜歡在 Umbraco 後台擴展的事情之一。不過我之前已經這樣做過,我發現如果我在 AngularJS 中的 Umbraco 的tinyMceService 上建立一個裝飾器,擴充編輯器是最簡單的。在 TinyMCE 的文檔中,我發現了一個名為「autoCompleters」的功能,它完全滿足了我的需要,因此我可以使用編輯器。我的初始程式碼(尚未進行任何測試)如下所示:


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;
}


我在這個專案中使用了 vite 和 typescript,但我沒有安裝任何 TinyMCE 類型。現在我將保留any並盡可能避免TinyMCE。

使用 TDD 進行構建

我決定使用 jest 進行測試。我發現了一個簡單的入門方法,很快就成功了。

✅ 成功
我學習了一種用於前端程式碼單元測試的新工具。我成功地應用該工具編寫了帶有單元測試的前端

我寫了我的第一個測試:

mention-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);
  });
});


打字稿編譯器的嚴格性讓我有些驚訝。此處的分步操作實際上意味著不添加任何您尚未實際使用的內容。例如,我想添加對“UI”的引用,因為我知道稍後會使用它,但在使用構造函數中放入的所有內容之前我無法實際編譯 MentionsManager。

經過幾輪紅、綠和重構,我最終得到了這些測試:

mention-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);
  });
});


有了這個邏輯,我可以從任何來源獲取提及並透過「fetch」掛鉤在 RTE 中顯示它們。
我使用相同的方法建立一個“pick”方法來獲取選定的成員並將提及插入到編輯器中。這是我最終得到的代碼:

mention-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);
  }
}


❓ 不確定性
Range 介面是一種內建類型,確實很難模擬,而該介面將實現細節洩漏到我的業務邏輯中。我覺得可能有更好的方法來做到這一點。

回顧

總的來說,我認為我最終得到了易於更改的簡單程式碼。這段程式碼中仍有一些部分我不太喜歡。我希望業務邏輯來驅動 UI,但程式碼最終更像是一個簡單的商店,它也對 UI 進行了一次呼叫。我想知道是否可以更牢固地包裝 UI,以便更好地利用管理器。

版本聲明 本文轉載於:https://dev.to/d_inventor/learning-tdd-by-doing-tagging-members-in-umbracos-rich-text-editor-29o4?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3