You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The project I am working on needs a bunch of UI components, and I wanted to make sure those components would work well with server-side rendering. Writing components for SSR involves a number of challenges: the components have to be hydration-safe, and (ideally) they should rely on HTML input elements as much as possible so that they can be used with createRouteAction.
As part of the development of these UI components, I needed an environment to try out the widgets as they are being developed. In the past I have used Storybook, as well as similar frameworks such as React-Cosmos. However, I needed a framework that would allow me to test both client-side and server-side rendering of the widgets. Using solid-start as a basis, I figured this couldn't be too hard, although it turns out that this was somewhat optimistic.
The name I chose for this tool is "Codex". The name is kind of a reference to Storybook - traditionally, a "codex" is a written document that is not a scroll, but rather a stack of pages bound on one edge - what we would today call a "book".
The basic idea is very simple: using Vite's import.meta.glob() function, we can search the codebase for all modules whose name matches the pattern *.fixture.tsx. Each of these fixture modules has a default export, which can either be a Solid component or a map of Solid components. (The latter allows multiple fixtures to be contained in a single source file.)
Terminology note: I'm using the name "fixture" rather than "story" because that's what React-Cosmos calls it, however this is merely a stylistic preference.
The fixture module can also export special symbols like $name and $category which controls how it is displayed in the tree view.
Once we have a list of all the fixtures, we can build a tree view of all the names. Clicking on a name will display the output of that fixture in the "canvas" pane. The fixture selection is done using solid-start routing (the fixture slug appears in the URL), which means that a browser refresh will do a server-side render of the fixture, while navigating in the tree view will do a client-side render. (I probably should make some explicit refresh buttons for this).
One wrinkle here is that the output of import.meta.glob() is not serializable, since it contains functions. This means that it cannot be transferred to the client as route data. However, we can transfer the list of keys since they are just strings. The client must then re-import the fixture module using the regular import() expression.
There is also an API that fixtures can call to create "adjustment parameters": widgets which appear in a pull out drawer which allow the user to interactively tweak the parameters of the fixture. There is also an event log feature which allows components being tested to print a message when a given event handler is fired.
So far the tool has proved to be extremely helpful in my project. I plan to make it available as an open-source package, however there are a number of intractable issues which I would like to fix before I make it generally available:
The biggest issue is that most users will want to install the tool as a dependency via npm rather than checking out the source code. Unfortunately, it is difficult to do this for several reasons:
Vite only knows how to find modules if the string being passed to import.meta.glob() is a constant. This makes it impossible for users to specify the location of their fixtures in a config file, they currently have to modify the source code. I considered using Vite aliases (#fixtureroot) however aliases don't support multiple paths, and multiple roots are a requirement (for me anyway).
The ability to hot reload components only works if we're running in Vite 'dev' mode. This means that users are going to have to execute Vite using a vite.config.js file that lives inside of node_modules. I don't want users to have to edit files inside of node_modules if they need to add their own configuration settings.
More generally, there are a lot of issues to be solved with having a solid-start application that is not at the apex of the project hierarchy.
There are a few additional challenges I haven't figured out:
Currently the canvas pane is part of the same browser frame as the Codex UI. This means that stylesheets and themes are shared between Codex's own UI and the component under test - for example, making changes to the "dark" theme would affect both the test component and the Codex UI. Ideally, the component being tested should live in an iframe so that it can be completely isolated, however this would require multiple entry points to the app, which I don't think solid-start currently supports.
This means that the API for adjustment widgets would have to be re-implemented using message events instead of direct calls.
Similarly, the current theme selector (light / dark mode) only works with my widget library, Dolmen. I would need to build a more general theme selector which could be configured by the user to work with their theming system.
Finally, because fixtures create adjustment widgets as a side-effect of rendering, this means that the adjustment panel doesn't always hydrate properly (a topic which I've discussed in another thread, so I won't go into details here.)
These issues are not a significant problem for me, but they would make it hard for anyone else to use the tool.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
The project I am working on needs a bunch of UI components, and I wanted to make sure those components would work well with server-side rendering. Writing components for SSR involves a number of challenges: the components have to be hydration-safe, and (ideally) they should rely on HTML input elements as much as possible so that they can be used with
createRouteAction.As part of the development of these UI components, I needed an environment to try out the widgets as they are being developed. In the past I have used Storybook, as well as similar frameworks such as React-Cosmos. However, I needed a framework that would allow me to test both client-side and server-side rendering of the widgets. Using solid-start as a basis, I figured this couldn't be too hard, although it turns out that this was somewhat optimistic.
The name I chose for this tool is "Codex". The name is kind of a reference to Storybook - traditionally, a "codex" is a written document that is not a scroll, but rather a stack of pages bound on one edge - what we would today call a "book".
The basic idea is very simple: using Vite's
import.meta.glob()function, we can search the codebase for all modules whose name matches the pattern*.fixture.tsx. Each of these fixture modules has a default export, which can either be a Solid component or a map of Solid components. (The latter allows multiple fixtures to be contained in a single source file.)Terminology note: I'm using the name "fixture" rather than "story" because that's what React-Cosmos calls it, however this is merely a stylistic preference.
The fixture module can also export special symbols like
$nameand$categorywhich controls how it is displayed in the tree view.Once we have a list of all the fixtures, we can build a tree view of all the names. Clicking on a name will display the output of that fixture in the "canvas" pane. The fixture selection is done using solid-start routing (the fixture slug appears in the URL), which means that a browser refresh will do a server-side render of the fixture, while navigating in the tree view will do a client-side render. (I probably should make some explicit refresh buttons for this).
One wrinkle here is that the output of
import.meta.glob()is not serializable, since it contains functions. This means that it cannot be transferred to the client as route data. However, we can transfer the list of keys since they are just strings. The client must then re-import the fixture module using the regularimport()expression.There is also an API that fixtures can call to create "adjustment parameters": widgets which appear in a pull out drawer which allow the user to interactively tweak the parameters of the fixture. There is also an event log feature which allows components being tested to print a message when a given event handler is fired.
So far the tool has proved to be extremely helpful in my project. I plan to make it available as an open-source package, however there are a number of intractable issues which I would like to fix before I make it generally available:
The biggest issue is that most users will want to install the tool as a dependency via npm rather than checking out the source code. Unfortunately, it is difficult to do this for several reasons:
import.meta.glob()is a constant. This makes it impossible for users to specify the location of their fixtures in a config file, they currently have to modify the source code. I considered using Vite aliases (#fixtureroot) however aliases don't support multiple paths, and multiple roots are a requirement (for me anyway).node_modules. I don't want users to have to edit files inside of node_modules if they need to add their own configuration settings.There are a few additional challenges I haven't figured out:
These issues are not a significant problem for me, but they would make it hard for anyone else to use the tool.
Once I figure out the solution to these problems, I will move Codex to its own package (currently it's a subpackage of Dolmen). However, if you want to look at the code, it lives here: https://github.com/viridia/dolmen/tree/main/packages/solid-codex
Beta Was this translation helpful? Give feedback.
All reactions