diff --git a/.changeset/twenty-fireants-collect.md b/.changeset/twenty-fireants-collect.md new file mode 100644 index 000000000..880e76782 --- /dev/null +++ b/.changeset/twenty-fireants-collect.md @@ -0,0 +1,5 @@ +--- +"druxt": minor +--- + +Changed template injected module components to not use a DruxtWrapper component by default. diff --git a/docs/content/guide/theming.md b/docs/content/guide/theming.md index 3c42331e7..7695cd9c3 100644 --- a/docs/content/guide/theming.md +++ b/docs/content/guide/theming.md @@ -51,10 +51,10 @@ If there are no matching component names, a default `DruxtWrapper` component wil Most Druxt modules can have the default template overridden, allowing for full control of the default slot rendering. -The available data provided to the template scope is deteremined by the relevant module. +The available data provided to the template scope is determined by the relevant module. ```vue - + ``` + +By default, a component using the default template will not be wrapped by a DruxtWrapper component. It is possible to enable the DruxtWrapper system by setting the `wrapper` property to `true`: + +```vue + + + +```` \ No newline at end of file diff --git a/examples/nuxt/wrappers/README.md b/examples/nuxt/wrappers/README.md new file mode 100644 index 000000000..669cca069 --- /dev/null +++ b/examples/nuxt/wrappers/README.md @@ -0,0 +1,10 @@ +# wrappers + +This directory contains exaples of how to use Wrapper components and the wrapper property. + + +## Setup + +``` +yarn && yarn dev +``` diff --git a/examples/nuxt/wrappers/components/druxt/entity/node/Page.vue b/examples/nuxt/wrappers/components/druxt/entity/node/Page.vue new file mode 100644 index 000000000..406230e8e --- /dev/null +++ b/examples/nuxt/wrappers/components/druxt/entity/node/Page.vue @@ -0,0 +1,16 @@ + + + diff --git a/examples/nuxt/wrappers/nuxt.config.js b/examples/nuxt/wrappers/nuxt.config.js new file mode 100644 index 000000000..7dceef17f --- /dev/null +++ b/examples/nuxt/wrappers/nuxt.config.js @@ -0,0 +1,3 @@ +export default { + buildModules: [['druxt-entity', { baseUrl: 'https://demo-api.druxtjs.org' }]] +} diff --git a/examples/nuxt/wrappers/package.json b/examples/nuxt/wrappers/package.json new file mode 100644 index 000000000..c9048d0a5 --- /dev/null +++ b/examples/nuxt/wrappers/package.json @@ -0,0 +1,11 @@ +{ + "name": "wrappers", + "scripts": { + "dev": "nuxt dev" + }, + "packageManager": "yarn@3.0.0", + "dependencies": { + "druxt-entity": "link:../../../packages/druxt-entity", + "nuxt": "latest" + } +} diff --git a/examples/nuxt/wrappers/pages/index.vue b/examples/nuxt/wrappers/pages/index.vue new file mode 100644 index 000000000..f02869475 --- /dev/null +++ b/examples/nuxt/wrappers/pages/index.vue @@ -0,0 +1,93 @@ + + + diff --git a/examples/nuxt/wrappers/yarn.lock b/examples/nuxt/wrappers/yarn.lock new file mode 100644 index 000000000..e69de29bb diff --git a/packages/druxt/src/components/DruxtModule.vue b/packages/druxt/src/components/DruxtModule.vue index 24ab5b483..f17459786 100644 --- a/packages/druxt/src/components/DruxtModule.vue +++ b/packages/druxt/src/components/DruxtModule.vue @@ -37,12 +37,25 @@ export default { default: null, }, + /** + * The wrapper component configuration. + * + * Used to set the wrapper component, class, style and propsData. + * + * @example + * + * + * @type {(Boolean|Object)} + */ wrapper: { - type: Object, - default: () => ({ - component: 'div', - propsData: {}, - }) + type: [Boolean, Object], + default: () => undefined }, }, @@ -97,7 +110,17 @@ export default { } // Build wrapper component object. - const options = this.getModuleComponents() + let options = [] + const hasDefaultTemplate = !!(this.$vnode.data.scopedSlots || {}).default + // Load wrapper components if: + if ( + // No default template and wrapper isn't false OR + (!hasDefaultTemplate && this.wrapper !== false) || + // Default tempalte and wrapper is set + (hasDefaultTemplate && this.wrapper) + ) { + options = this.getModuleComponents() + } let component = { is: (((options.filter(o => o.global) || [])[0] || {}).name || 'DruxtWrapper'), options: options.map(o => o.name) || [], @@ -154,12 +177,14 @@ export default { * @returns {Components} */ getModuleComponents() { + // Ensure that the Druxt module component has `druxt.componentOptions`. if (!(this.$options.druxt || {}).componentOptions) { return [] } - const options = this.$options.druxt.componentOptions.call(this, this) - if (!options || !options.length) { + + // Ensure that there available component options are returned. + if (!(options || []).length) { return [] } @@ -304,20 +329,27 @@ export default { const self = this const wrapperData = { - class: this.wrapper.class || undefined, - style: this.wrapper.style || undefined, - props: this.wrapper.propsData, + class: (this.wrapper || {}).class || undefined, + style: (this.wrapper || {}).style || undefined, + props: (this.wrapper || {}).propsData || undefined, } // Return only wrapper if fetch state is still pending. if (this.$fetchState.pending) { - return h(this.wrapper.component, wrapperData) + return h((this.wrapper || {}).component || 'div', wrapperData) } - // Return wrapped component. + // Prepare attributes. const attrs = { ...this.component.$attrs, ...this.$attrs } delete attrs['data-fetch-key'] - return h(this.wrapper.component, wrapperData, [ + + // Unwrap default template based component if required. + if ((this.$scopedSlots.default && !this.wrapper) || this.wrapper === false) { + this.component.is = 'DruxtWrapper' + } + + // Return component. + return h((this.wrapper || {}).component || 'div', wrapperData, [ h(this.component.is, { attrs, on: { diff --git a/packages/druxt/test/components/DruxtModule.test.js b/packages/druxt/test/components/DruxtModule.test.js index 8d1ab1fab..97203e5b6 100644 --- a/packages/druxt/test/components/DruxtModule.test.js +++ b/packages/druxt/test/components/DruxtModule.test.js @@ -13,6 +13,7 @@ describe('DruxtModule component', () => { mocks = { $createElement: jest.fn(), $fetchState: { pending: false }, + $options: { druxt: {} }, $nuxt: { context: { isDev: false, @@ -199,11 +200,19 @@ describe('DruxtModule component', () => { }) test('custom default slot', async () => { + const customModule = { + extends: DruxtModule, + druxt: {} + } + const scopedSlots = { default: jest.fn() } - const wrapper = mount(DruxtModule, { localVue, mocks, scopedSlots }) + const wrapper = mount(customModule, { localVue, mocks, scopedSlots }) await wrapper.vm.$options.fetch.call(wrapper.vm) wrapper.vm.getScopedSlots().default.call(wrapper.vm) expect(scopedSlots.default).toHaveBeenCalled() + + await wrapper.setProps({ wrapper: true }) + await wrapper.vm.$options.fetch.call(wrapper.vm) }) })