Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add component instances to slots on mount #41

Closed
alexjoverm opened this issue Sep 5, 2017 · 10 comments
Closed

Add component instances to slots on mount #41

alexjoverm opened this issue Sep 5, 2017 · 10 comments

Comments

@alexjoverm
Copy link

Is there a way to somehow pass component instances to the slots property of mount? The use case is, say I have a component that has a required property:

export default {
    name: 'Message',
    props: {
      message: {
        type: String,
        required: true,
        validator: value => value.length > 1
      }
  }

Then if in a test I wanna add a list of Message as the default slots, I cannot do it like it's documented: I'll get a "message" property is required error:

mount(MessageList, {
  slots: {
    default: messages
  }
})

I tried using mount and getting the vm out of it, or also extending the component and using new Vue(...) but I get the error [Vue warn]: Failed to mount component: template or render function not defined.:

beforeEach(() => {
  const indexes = [0, 1, 2]
  const messages = indexes.map(i => mount(Message, {
    propsData: { message: `Message ${i}` }
  }).vm)

  cmp = mount(MessageList, {
    slots: {
      default: messages
    }
  })
})

So, how's it possible to accomplish that?

@eddyerburgh
Copy link
Member

There isn't currently a way to pass mounted components to slots.

There's some work being done in avoriaz to add the option to pass Wrappers as slots. But right now there's an unsolved bug - eddyerburgh/avoriaz#113.

I'd like to add this functionality, but don't have time to work on this feature right now. I'd welcome a PR and could give you some guidance if you were willing to make one.

@alexjoverm
Copy link
Author

Thanks, right now I also don't have the time, but probably in the coming days I can get some and lend a hand in there. I'll contact you for that guidance in that case ;).

@alexjoverm
Copy link
Author

FYI, as a workaround, this seems to work:

mount(MessageList, {
  slots: {
    default: {
      render(h) {
        return h(Message, { props: { message: 'hey' }  })
      }
    }
  }
})

@sebastiandedeyne
Copy link

Hmm this doesn't seen to work for me. The component is created, but the props are undefined when I try to access them...

const table = mount(TableComponent, {
    propsData: {
        data: [
            { firstName: 'John', lastName: 'Lennon' },
            { firstName: 'Paul', lastName: 'McCartney' },
            { firstName: 'George', lastName: 'Harrison' },
            { firstName: 'Ringo', lastName: 'Starr' },
        ],
    },
    slots: {
        default: {
            render(h) {
                return h(TableColumn, {
                    props: {
                        show: 'firstName',
                        label: 'First name',
                    },
                });
            },
        },
    },
});

console.log(table.vm.$slots.default[0].componentInstance.show);

@mouafa
Copy link

mouafa commented Dec 8, 2017

try this:

const tableCol = table.find(TableColumn)
expect(tableCol.vm.show).toBe('firstName')
expect(tableCol.vm.label).toBe('First name')

@eddyerburgh
Copy link
Member

We aren't going to implement this at the moment. You can pass a component, but not an instance.

@phobetron
Copy link

@eddyerburgh Is this functionality anywhere on the roadmap? I have some functionality that can't really be tested without access to instance properties, and workarounds don't always work well.

@eddyerburgh
Copy link
Member

It's not on the roadmap. I would review a PR if somebody wants to add it.

@hiendv
Copy link
Contributor

hiendv commented Jul 5, 2018

If anyone is looking for a workaround, try testing with another Component

let App = Vue.extend({
  render (h) {
    return h(Parent, {
      props: {
        // parent props
      }
    }, [
      // slots
      h(Child, {
        props: {
          // child props
        }
      })
    ])
  }
})

it('has a button', () => {
  let wrapper = mount(App)
  console.log(wrapper.find(Parent).vm.$slots.default)
})

@hiendv
Copy link
Contributor

hiendv commented Aug 16, 2019

Oh snap, I just figured it out how to do it properly. Try this.

const localVue = createLocalVue()
localVue.component('child', Child)

const wrapper = mount(Parent, {
  slots: {
    default: `
      <child foo="bar"><div>Lorem ipsum</div></child>
      <child foo="qux"><div>Dolor sit amet</div></child>
    `
  },
  localVue
})

localVue was there for the whole time but I kept trying to mount slots with createElement. Damn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants