私が構築しているシステムでは、Web サイト内のテキストで Umbraco メンバーに言及する機能が必要です。そのためには、Umbraco のリッチ テキスト エディターの拡張機能である TinyMCE.
を構築する必要があります。コンテンツ編集者として、メッセージまたは記事内でメンバーにタグを付けて、メンバーに関する新しいコンテンツに関する通知を受信できるようにしたいと考えています。
Slack や X などの同様の実装を調べました。Slack は書き込み中のメンションに特別な HTML タグを使用しますが、その後、特定の形式のトークンを使用してデータをバックエンドに送信します。私も同様のアプローチを取ることにしましたが、今のところは翻訳ステップのことは忘れてください。コンテンツでは、メンションは次のようになります:
@D_Inventor
構築を始める前に、Umbraco で TinyMCE に接続する方法を探していました。これは、Umbraco バックオフィスで拡張したくないものの 1 つです。ただし、以前にこれを実行したことがあります。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 のタイプがインストールされていません。今のところ、私はすべてを保持し、TinyMCE をできるだけ避けるようにします。
テストのために jest を使用することにしました。簡単に始めることができ、すぐに何かを動作させることができました。
✅ 成功 |
---|
フロントエンド コードの単体テストのための新しいツールを学びました。ツールを適用して単体テストを含むフロントエンドを作成することに成功しました |
最初のテストを書きました:
メンションマネージャー.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); }); });
typescript コンパイラの厳密さには少々驚きました。ここで段階的に作業するということは、実際には、まだ実際に使用していないものを追加しないことを意味します。たとえば、「UI」への参照を追加したいと思ったのは、それを後で使用することがわかっていたからですが、コンストラクターに入力したものをすべて使用するまで、MentionsManager を実際にコンパイルすることはできませんでした。
赤、緑、リファクタリングを数ラウンド行った後、次のテストが完成しました:
メンションマネージャー.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); }); });
このロジックを導入すると、任意のソースからメンションをフェッチし、「フェッチ」フックを通じて RTE に表示できます。
同じアプローチを使用して、選択したメンバーを取得し、エディターにメンションを挿入する「pick」メソッドを作成しました。これは私が最終的に得たコードです:
メンションマネージャー.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 への呼び出しを 1 回行う単純なストアのようになりました。マネージャーをもっと活用するために、UI をもっと強力にラップできないだろうか。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3