Repository: chanind/hanzi-writer
Pull request #223 (2021-03-02 21:13:11)

adding a getCharacterData() method for easier access to loaded character data
This PR adds a method `writer.getCharacterData()`, which returns a promise containing the parsed character data after it's loaded. This is to provide a more usable method to get info about the currently loaded character without needing to do anything hacky around reading internals like `writer._character`. This promise will always return the character data or error if it's not possible.

This character data contains the currently loaded character symbol (ex `我`), and an array of `strokes` containing the stroke data needed to render the character. This is probably most useful to determine how many strokes there are in the character.

related to #222


Patch:
diff --git a/src/HanziWriter.ts b/src/HanziWriter.ts
index c5d92ed5..69c66cdb 100644
--- a/src/HanziWriter.ts
+++ b/src/HanziWriter.ts
@@ -419,6 +419,14 @@ export default class HanziWriter {
     return this._withDataPromise;
   }
 
+  async getCharacterData(): Promise<Character> {
+    if (!this._char) {
+      throw new Error('setCharacter() must be called before calling getCharacterData()');
+    }
+    const character = await this._withData(() => this._character);
+    return character!;
+  }
+
   _assignOptions(options: Partial<HanziWriterOptions>): ParsedHanziWriterOptions {
     const mergedOptions = {
       ...defaultOptions,


Test patch:
diff --git a/src/__tests__/HanziWriter-test.ts b/src/__tests__/HanziWriter-test.ts
index 5cfd2785..2a0861fd 100644
--- a/src/__tests__/HanziWriter-test.ts
+++ b/src/__tests__/HanziWriter-test.ts
@@ -9,6 +9,7 @@ import HanziWriter from '../HanziWriter';
 import { timeout } from '../utils';
 import { resolvePromises } from '../testUtils';
 import Quiz from '../Quiz';
+import parseCharData from '../parseCharData';
 
 const charDataLoader = () => ren;
 
@@ -225,6 +226,60 @@ describe('HanziWriter', () => {
     });
   });
 
+  describe('getCharacterData', () => {
+    it('returns a promise with the loaded character', async () => {
+      const writer = HanziWriter.create('target', '人', {
+        charDataLoader,
+      });
+      const character = await writer.getCharacterData();
+      expect(character).toEqual(parseCharData('人', ren));
+    });
+
+    it('returns a promise with the loaded character after initial loading is done', async () => {
+      const writer = HanziWriter.create('target', '人', {
+        charDataLoader,
+      });
+      await writer._withDataPromise;
+      const character = await writer.getCharacterData();
+      expect(character).toEqual(parseCharData('人', ren));
+    });
+
+    it('errors if no character has been set yet', async () => {
+      const writer = new HanziWriter('target', {
+        charDataLoader,
+      });
+      await expect(writer.getCharacterData()).rejects.toThrow(
+        new Error('setCharacter() must be called before calling getCharacterData()'),
+      );
+    });
+
+    it('errors if loading fails', async () => {
+      const writer = HanziWriter.create('target', '人', {
+        charDataLoader: async () => {
+          throw new Error('omg!');
+        },
+      });
+
+      await expect(writer.getCharacterData()).rejects.toThrow(new Error('omg!'));
+    });
+
+    it('errors if loading fails before this method is called', async () => {
+      const writer = HanziWriter.create('target', '人', {
+        charDataLoader: async () => {
+          throw new Error('omg!');
+        },
+      });
+      try {
+        await writer._withDataPromise;
+        // eslint-disable-next-line no-empty
+      } catch (err) {}
+
+      await expect(writer.getCharacterData()).rejects.toThrow(
+        new Error('Failed to load character data. Call setCharacter and try again.'),
+      );
+    });
+  });
+
   describe('animateCharacter', () => {
     it('animates and returns promise that resolves when animation is finished', async () => {
       document.body.innerHTML = '<div id="target"></div>';