Awesome
Surface Catalogue
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.3", 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:
mix dev
or using iex
:
iex -S mix dev
You can now access the catalogue at localhost:4000.
If you need, you can also start the server using a different port:
PORT=4444 iex -S mix dev
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.