Volto: Composition to customize components?

Hi,

I want to do a minimal change in the ObjectBrowserBody component (set currentFolder initially to the context URL for every mode). Using component shadowing works well, but copying the whole components code just to change a single line does not feel very neat.

Is there a way to use the original component as base for my customized one (for example via composition/inheritance)?

Thanks!

yeah :slight_smile: we have several tickets in the Volto tracker basically stating this exact need: #1 and 2 and who knows how many more.

There is a webpack alias that could be used to access the original module, the @plone/volto-original. So in theory (if that component is properly composable), you could import the original ObjectBrowserBody in your customization. I don't know if this would actually work, it would be good to see some reports on this topic. The ugly side is, though, that the initial path is not a prop so first order of business would be to make it a prop (and a PR to Volto about this).

1 Like

Thank you for your quick answer and pointing me to this issues! 2 would be a nice feature to make Volto even more awesome :wink:

Defining the alias in my projects .eslintrc.js did not seem to work. So I had to extend razzle.config.js like this (razzle.extend.js only works for addons right?)

When the alias is defined you are able to import the original component as ObjectBrowserBodyOrig and override its constructor:

import ObjectBrowserBodyOrig from '@plone/volto-original/components/manage/Sidebar/ObjectBrowserBody';

import { compose } from 'redux';
import { connect } from 'react-redux';
import { injectIntl } from 'react-intl';
import { searchContent } from '@plone/volto/actions/search/search';

import { join } from 'lodash';

/* since ObjectBrowserBodyOrig is a higher order component we need to use `.WrappedComponent` as base class here */
class ObjectBrowserBody extends ObjectBrowserBodyOrig.WrappedComponent {

  constructor(props) {
    super(props);
    this.state.currentFolder = this.props.contextURL || '/';
    this.state.parentFolder = `${join(this.state.currentFolder.split('/').slice(0, -1), '/')}` || '/';
  }

}

export default compose(
  injectIntl,
  connect(
    (state) => ({
      searchSubrequests: state.search.subrequests,
    }),
    { searchContent },
  ),
)(ObjectBrowserBody);
1 Like

Unfortunately js development world is a hodge-podge of configuration file with sometimes overlapping settings, for example the webpack aliases. .eslintrc.js is used to configure eslint, which is the linter/code formatting tool (think flake8). The proper way to add an alias is indeed with a custom razzle.config.js (in projects) or razzle.extend.js in addons.

I take it you weren't able to use the volto-original alias? Too bad... the path to import would be @plone/volto-original/components/manage/Sidebar/ObjectBrowserBody. Anyway... awesome you were able to make it work, and for documenting this.

Yep this config juggling can be a bit confusing sometimes :smiley:

Anyways, using @plone/volto-original/components/manage/Sidebar/ObjectBrowserBody works as well and does not require any config adaption (I simply did not know about this possibility). Will update the comment above to avoid confusion.