This is mostly a prototype, meant to validate a few ideas to have something similar to https://storybook.js.org/ for Surface.
Installation
Add surface_catalogue to your list of dependencies in mix.exs:
def deps do [ {:surface_catalogue, "~> 0.6.4", only: :dev} ] end
Update your router.ex configuration:
# lib/my_app_web/router.ex use MyAppWeb, :router import Surface.Catalogue.Router ... if Mix.env() == :dev do scope "/" do pipe_through :browser surface_catalogue "/catalogue" end end
Add a :catalogue entry in the :esbuild config in config.exs:
config :esbuild, default: ..., # Add a new entry for the catalogue catalogue: [ args: ~w(../deps/surface_catalogue/assets/js/app.js --bundle --target=es2016 --minify --outdir=../priv/static/assets/catalogue), cd: Path.expand("../assets", __DIR__), env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ]
Then update the endpoint configuration in config/dev.exs to add an esbuild watcher
for catalogue:
config :my_app, MyAppWeb.Endpoint, ... watchers: [ esbuild: ..., # Add an esbuild watcher for :catalogue esbuild: {Esbuild, :install_and_run, [:catalogue, ~w(--sourcemap=inline --watch)]}, ]
That's all!
Run mix phx.server and access "/catalogue" to see the list of all available components in
your project.
Loading Examples and Playgrounds
If you want to access examples and playgrounds for components, edit your mix.exs file,
adding a new entry for elixirc_paths along with a catalogues function listing the
catalogues you want to be loaded:
... def catalogues do [ # Local catalogue "priv/catalogue", # Dependencies catalogues "deps/surface/priv/catalogue", "deps/surface_bulma/priv/catalogue", # External catalogues Path.expand("../my_components/priv/catalogue"), "/Users/johndoe/workspace/other_components/priv/catalogue" ] end defp elixirc_paths(:dev), do: ["lib"] ++ catalogues() ...
Then update the endpoint configuration in config/dev.exs to set up live reloading
for your catalogue:
config :my_app, MyAppWeb.Endpoint, live_reload: [ patterns: [ ~r"priv/catalogue/.*(ex)$", ... ] ]
Note: Without the above configurations, the list of available components is still presented in the catalogue page. However, when selecting a component, only its documentation and API will be shown. No example/playground will be loaded nor tracked by Phoenix's live reloader.
Sharing catalogues
If you're working on a suite of components that you want to share as a library, you
may need to provide additional information about the catalogue. This will be necessary
whenever your components require any css or js code that might not be available
on the host project.
To provide that additional information you must create a module implementing the
Surface.Catalogue behaviour in your priv/catalogue/ folder. Example:
defmodule MySuite.Catalogue do use Surface.Catalogue @impl true def config() do [ head_css: """ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.8.2/css/bulma.min.css" /> """ ] end end
Running the built-in catalogue server
In case you're working on a lib that doesn't initialize its own Phoenix endpoint, you
can use the built-in server provided by the surface_catalogue following these steps:
Add the required dependencies to your list of dependencies in mix.exs:
def deps do [ {:plug_cowboy, "~> 2.0", only: :dev}, {:phoenix_live_reload, "~> 1.2", only: :dev}, {:jason, "~> 1.0", only: :dev} ] end
Create a dev.exs script at the root of your project with the following content:
# iex -S mix dev Logger.configure(level: :debug) # Start the catalogue server Surface.Catalogue.Server.start( watchers: [ esbuild: {Esbuild, :install_and_run, [:catalogue, ~w(--sourcemap=inline --watch)]} ], live_reload: [ patterns: [ ~r"priv/static/.*(js|css)$", ~r"lib/my_lib_web/.*(ex)$" ], notify: [ live_view: [ # Colocated sface and css can be reloaded without a hard refresh ~r"lib/my_lib_web/.*(sface|css)$" ] ] ] )
Surface Catalogue's assets need to be generated by your project to make it compatible with your Phoenix dependency versions.
Add the following content to your config/config.exs or config/dev.exs:
import Config if Mix.env() == :dev do config :surface_catalogue, title: "My Lib Web", config :esbuild, ... catalogue: [ args: ~w(#{Mix.Project.deps_paths().surface_catalogue}/assets/js/app.js --bundle --target=es2016 --minify --outdir=priv/static/assets/catalogue), env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} ] # Required at compile time config :surface_catalogue, Surface.Catalogue.Server.Endpoint, code_reloader: true, debug_errors: true end
Make sure you set the patterns option according to your project.
To make things easier, add an alias run the script in your mix.exs:
def project do [ ..., aliases: aliases() ] end ... defp aliases do [ dev: "run --no-halt dev.exs", ... ] end
Run the server with:
or using iex:
You can now access the catalogue at localhost:4000.
If you need, you can also start the server using a different port:
Credits
The Surface.Catalogue.Server implementation was mostly extracted from the dev.exs script
from phoenix_live_dashboard.
All credits to the Phoenix Core Team.
License
Copyright (c) 2021, Marlus Saraiva.
Surface source code is licensed under the MIT License.