1- import { getMerkur } from '@merkur/core' ;
1+ import { getMerkur , createMerkurWidget } from '@merkur/core' ;
22
3+ /**
4+ * Creates a loader function for Storybook that manages Merkur widget lifecycle.
5+ * The loader creates and mounts widgets for stories, reusing instances when possible
6+ * and unmounting previous widgets when switching stories.
7+ *
8+ * @param {Object } options Configuration options
9+ * @param {Object } options.widgetProperties The widget properties used to create widget instances
10+ * @param {Function } options.render Callback function called when widget state updates (receives widget instance)
11+ * @returns {Function } Async loader function that returns widget instance for stories
12+ */
313function createWidgetLoader ( { render, widgetProperties } ) {
414 let lastStory = { } ;
515
@@ -21,7 +31,7 @@ function createWidgetLoader({ render, widgetProperties }) {
2131 . then ( async ( widget ) => {
2232 widget . $in . component . lifeCycle . mount = ( ) => { } ;
2333 widget . $in . component . lifeCycle . update = ( ) => {
24- render ( ) ;
34+ render ( widget ) ;
2535 } ;
2636 widget . $in . component . lifeCycle . unmount = ( ) => { } ;
2737
@@ -40,4 +50,108 @@ function createWidgetLoader({ render, widgetProperties }) {
4050 } ;
4151}
4252
43- export { createWidgetLoader } ;
53+ /**
54+ * Creates a Storybook preview configuration for Merkur widgets.
55+ * Handles widget registration and loader setup.
56+ *
57+ * @param {Object } options Configuration options
58+ * @param {Object } options.widgetProperties The widget properties to register
59+ * @param {Function } [options.render] Optional custom render function for widget updates (receives widget instance)
60+ * @param {Function } [options.createWidget=createMerkurWidget] Factory function to create widget instances
61+ * @returns {Object } Storybook preview configuration with loaders
62+ */
63+ function createPreviewConfig ( {
64+ widgetProperties,
65+ render,
66+ createWidget = createMerkurWidget ,
67+ } ) {
68+ // Register the widget with Merkur
69+ getMerkur ( ) . register ( {
70+ ...widgetProperties ,
71+ createWidget,
72+ } ) ;
73+
74+ return {
75+ loaders : [
76+ createWidgetLoader ( {
77+ widgetProperties,
78+ render : render || ( ( ) => { } ) ,
79+ } ) ,
80+ ] ,
81+ } ;
82+ }
83+
84+ /**
85+ * Creates a render function for vanilla JavaScript widgets with state management.
86+ * Renders widget using HTML string output and handles re-rendering.
87+ *
88+ * @param {Object } options Configuration options
89+ * @param {Function|Object } options.ViewComponent The default view function or component map
90+ * @param {Function } options.bindEvents Function to bind events to the container
91+ * @returns {Object } Object with render function and update callback
92+ */
93+ function createVanillaRenderer ( options ) {
94+ const { ViewComponent, bindEvents } = options ;
95+
96+ // Store references for re-rendering
97+ let currentContainer = null ;
98+ let currentWidget = null ;
99+ let currentViewFunction = null ;
100+
101+ function getViewFunction ( args , ViewComponent ) {
102+ if ( typeof ViewComponent === 'function' ) {
103+ return ViewComponent ;
104+ }
105+
106+ if ( args . viewComponent && ViewComponent [ args . viewComponent ] ) {
107+ return ViewComponent [ args . viewComponent ] ;
108+ }
109+
110+ if ( args . component ) {
111+ return typeof args . component === 'function'
112+ ? args . component
113+ : ViewComponent [ args . component ] ;
114+ }
115+
116+ return ViewComponent . default || ViewComponent ;
117+ }
118+
119+ function renderWidget ( container , widget , viewFunction ) {
120+ container . innerHTML = viewFunction ( widget ) ;
121+
122+ if ( bindEvents ) {
123+ bindEvents ( container , widget ) ;
124+ } else if ( widget . View ?. bindEvents ) {
125+ widget . View . bindEvents ( container , widget ) ;
126+ }
127+ }
128+
129+ return {
130+ render : ( args , { loaded : { widget } } ) => {
131+ if ( ! widget ) {
132+ const empty = document . createElement ( 'div' ) ;
133+ empty . textContent = 'Loading widget...' ;
134+ return empty ;
135+ }
136+
137+ const container = document . createElement ( 'div' ) ;
138+ const viewFunction = getViewFunction ( args , ViewComponent ) ;
139+
140+ renderWidget ( container , widget , viewFunction ) ;
141+
142+ // Store references for re-rendering
143+ currentContainer = container ;
144+ currentWidget = widget ;
145+ currentViewFunction = viewFunction ;
146+
147+ return container ;
148+ } ,
149+ update : ( ) => {
150+ if ( currentContainer && currentWidget && currentViewFunction ) {
151+ renderWidget ( currentContainer , currentWidget , currentViewFunction ) ;
152+ }
153+ } ,
154+ } ;
155+ }
156+
157+ export { createWidgetLoader , createPreviewConfig , createVanillaRenderer } ;
0 commit comments