From acf31cb4969ac4d867852d17e8acb78ba484eddc Mon Sep 17 00:00:00 2001 From: Luke Melia Date: Sun, 16 Oct 2022 23:15:15 -0400 Subject: [PATCH] Provide alternative to inheritance for customizing component paths Recommending inheritance for Glimmer components is not a good practice. This commit adds component arguments to the RenderMobiledoc component that allow a user to achieve the same results via composition. The README is updated accordingly and also converted to use angle bracket syntax in its example code. --- README.md | 39 +++++++++++------ .../components/render-mobiledoc/component.js | 10 ++++- .../components/render-mobiledoc-test.js | 43 ++++++++++++++++++- 3 files changed, 74 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index a8a855d..f319698 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Provides: - * Component `{{render-mobiledoc}}` for rendering mobiledoc in your ember app + * Component `` for rendering mobiledoc in your ember app * (For advanced use) The ability to import the [`mobiledoc-dom-renderer`](https://github.com/bustlelabs/mobiledoc-dom-renderer) class To learn more about mobiledoc see [mobiledoc-kit](https://github.com/bustlelabs/mobiledoc-kit). @@ -23,7 +23,7 @@ To learn more about mobiledoc see [mobiledoc-kit](https://github.com/bustlelabs/ #### Render basic mobiledoc in your template ```hbs -{{render-mobiledoc mobiledoc=myMobileDoc}} + ``` #### Render mobiledoc with cards, using ember components to render cards @@ -31,7 +31,7 @@ To learn more about mobiledoc see [mobiledoc-kit](https://github.com/bustlelabs/ ```hbs {{! myMobiledoc is the mobiledoc you want to render }} {{! myCardNames is an array of card names, e.g. ['embed-card', 'slideshow-card'] }} -{{render-mobiledoc mobiledoc=myMobileDoc cardNames=myCardNames}} + ``` The ember components with names matching the mobiledoc card names will be rendered @@ -40,19 +40,30 @@ The ember components will be in a wrapper div with the class '__rendered-mobiled #### Customizing card lookup -If your mobiledoc card names do not match component names, you can subclass -the `render-mobiledoc` component and override its `cardNameToComponentName` method. +If your mobiledoc card names do not match component names, you can pass an argument to +the `` component to provide your own mapping. E.g.: +```hbs +// components/my-component.hbs + +``` + ```javascript -// components/my-render-mobiledoc.js -import RenderMobiledoc from 'ember-mobiledoc-dom-renderer/components/render-mobiledoc'; -export default RenderMobiledoc.extend({ +// components/my-component.js +import Component from '@glimmer/component'; +import { action } from '@ember/object'; + +export default class extends Component { + @action cardNameToComponentName(mobiledocCardName) { return 'cards/' + mobiledocCardName; } -}); +} ``` #### Render mobiledoc with atoms, using ember components to render atoms @@ -61,13 +72,13 @@ This works the same way as rendering mobiledoc with ember components for cards. To pass atom names to the renderer, use the `atomNames` property, e.g.: ```hbs {{! myAtomNames is an array of atom names, e.g. ['mention-atom'] }} -{{render-mobiledoc mobiledoc=myMobileDoc atomNames=myAtomNames}} + ``` The component will be passed a `payload` and `value` property. -To customize atom lookup, extend the `render-mobiledoc` component and override -its `atomNameToComponentName` method. +To customize atom lookup, pass an `atomNameToComponentName` argument similar to +what is shown above for `cardNameToComponentName`. #### Customizing markup and section rendering The `sectionElementRenderer` and `markupElementRenderer` options can be used to @@ -76,7 +87,7 @@ customize the elements used for sections and inline text decorations respectivel E.g.: ```hbs -{{render-mobiledoc mobiledoc=myMobileDoc sectionElementRenderer=mySectionElementRenderer}} + ``` ```js @@ -92,7 +103,7 @@ mySectionElementRenderer: { #### Use mobiledoc-dom-renderer directly This addon provides the mobiledoc-dom-renderer directly. Most of the time -you will want to use the `{{render-mobiledoc}}` component, but if you need +you will want to use the `` component, but if you need to use the renderer directly in code, it can be imported: `import DOMRenderer from 'ember-mobiledoc-dom-renderer'`; diff --git a/addon/components/render-mobiledoc/component.js b/addon/components/render-mobiledoc/component.js index e4707ae..a9cc5f5 100644 --- a/addon/components/render-mobiledoc/component.js +++ b/addon/components/render-mobiledoc/component.js @@ -192,13 +192,19 @@ export default class extends Component { return super.willDestroy(...arguments); } - // override in subclass to change the mapping of card name -> component name + // pass the argument to change the mapping of card name -> component name cardNameToComponentName(name) { + if (this.args.cardNameToComponentName) { + return this.args.cardNameToComponentName(name); + } return name; } - // override in subclass to change the mapping of atom name -> component name + // pass the argument to change the mapping of atom name -> component name atomNameToComponentName(name) { + if (this.args.atomNameToComponentName) { + return this.args.atomNameToComponentName(name); + } return name; } diff --git a/tests/integration/components/render-mobiledoc-test.js b/tests/integration/components/render-mobiledoc-test.js index 17032ee..2d4ee59 100644 --- a/tests/integration/components/render-mobiledoc-test.js +++ b/tests/integration/components/render-mobiledoc-test.js @@ -45,7 +45,7 @@ module('Integration | Component | render-mobiledoc', function (hooks) { .exists(`renders card with class ${CARD_ELEMENT_CLASS}-${cardName}`); }); - test('it uses `cardNameToComponentName` to allow selecting components', async function (assert) { + test('it uses `cardNameToComponentName` to allow selecting components (inheritance)', async function (assert) { this.set('mobiledoc', createMobiledocWithCard(cardName)); this.set('cardNames', [cardName]); @@ -62,6 +62,24 @@ module('Integration | Component | render-mobiledoc', function (hooks) { .hasText('foo: bar', 'renders card payload'); }); + test('it uses `cardNameToComponentName` to allow selecting components (arg)', async function (assert) { + this.set('mobiledoc', createMobiledocWithCard(cardName)); + this.set('cardNames', [cardName]); + this.set('cardNameToComponentName', (cardName) => { + return cardName.replace('sample', 'sample-changed-name'); + }); + await render( + hbs`` + ); + assert.dom('#sample-test-card').doesNotExist(); + assert + .dom('#sample-changed-name-test-card') + .exists('renders card template'); + assert + .dom('#sample-changed-name-test-card') + .hasText('foo: bar', 'renders card payload'); + }); + test('it renders mobiledoc with atoms', async function (assert) { this.set('mobiledoc', createMobiledocWithAtom(atomName)); this.set('atomNames', [atomName]); @@ -84,7 +102,7 @@ module('Integration | Component | render-mobiledoc', function (hooks) { .exists(`renders atom with class ${ATOM_ELEMENT_CLASS}-${atomName}`); }); - test('it uses `atomNameToComponentName` to allow selecting components', async function (assert) { + test('it uses `atomNameToComponentName` to allow selecting components (inheritance)', async function (assert) { this.set('mobiledoc', createMobiledocWithAtom(atomName)); this.set('atomNames', [atomName]); @@ -105,6 +123,27 @@ module('Integration | Component | render-mobiledoc', function (hooks) { .includesText('payload: bar', 'renders atom payload'); }); + test('it uses `atomNameToComponentName` to allow selecting components (arg)', async function (assert) { + this.set('mobiledoc', createMobiledocWithAtom(atomName)); + this.set('atomNames', [atomName]); + this.set('atomNameToComponentName', (atomName) => { + return atomName.replace('sample', 'sample-changed-name'); + }); + await render( + hbs`` + ); + assert.dom('#sample-test-atom').doesNotExist(); + assert + .dom('#sample-changed-name-test-atom') + .exists('renders atom template'); + assert + .dom('#sample-changed-name-test-atom') + .includesText('value: value', 'renders atom value'); + assert + .dom('#sample-changed-name-test-atom') + .includesText('payload: bar', 'renders atom payload'); + }); + test("it does not rerender if a atom component changes its card's payload or value", async function (assert) { let inserted = 0; let atom;