StimulusWrapper.tsx 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import type { Application, Definition } from '@hotwired/stimulus';
  2. import React from 'react';
  3. import { initStimulus } from '../src/includes/initStimulus';
  4. /**
  5. * Wrapper around the Stimulus application to ensure that the application
  6. * is scoped to only the specific story instance's DOM and also ensure
  7. * that the hot-reloader / page switches to not re-instate new applications
  8. * each time.
  9. *
  10. * @example
  11. * import { StimulusWrapper } from '../storybook/StimulusWrapper';
  12. * const Template = ({ debug }) =>
  13. * <StimulusWrapper
  14. * definitions={[{ controllerConstructor: SubmitController, identifier: 'w-something' }]}
  15. * debug={debug}
  16. * >
  17. * <form data-controller="w-something" />
  18. * </StimulusWrapper>
  19. */
  20. export class StimulusWrapper extends React.Component<{
  21. debug?: boolean;
  22. definitions?: Definition[];
  23. }> {
  24. ref: React.RefObject<HTMLDivElement>;
  25. application?: Application;
  26. constructor(props) {
  27. super(props);
  28. this.ref = React.createRef();
  29. }
  30. componentDidMount() {
  31. const { debug = false, definitions = [] } = this.props;
  32. const root = this.ref.current || document.documentElement;
  33. this.application = initStimulus({ debug, definitions, root });
  34. }
  35. componentDidUpdate({ debug: prevDebug }) {
  36. const { debug } = this.props;
  37. if (debug !== prevDebug) {
  38. Object.assign(this.application as Application, { debug });
  39. }
  40. }
  41. componentWillUnmount() {
  42. if (!this.application) return;
  43. this.application.stop();
  44. delete this.application;
  45. }
  46. render() {
  47. const { children } = this.props;
  48. return <div ref={this.ref}>{children}</div>;
  49. }
  50. }