Skip to content

Commit

Permalink
feat: wrap the whole notebook in jp-Notebook div
Browse files Browse the repository at this point in the history
CSS, if any, is now written during the call to WrapAll.
This only happens once per notebook, so wrapping CSSWriter
into WriterOnce is redundant.

Removed WriterOnce, it's not used anywhere else.

Added a test case for SVG images in html.Renderer.
  • Loading branch information
bevzzz committed Feb 27, 2024
1 parent 240c053 commit b51732d
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 40 deletions.
1 change: 1 addition & 0 deletions pkg/test/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type fakeWrapper struct{}
var _ render.CellWrapper = (*fakeWrapper)(nil)

func (*fakeWrapper) RegisterFuncs(render.RenderCellFuncRegistry) {}
func (*fakeWrapper) WrapAll(io.Writer, func(io.Writer) error) error { return nil }
func (*fakeWrapper) Wrap(w io.Writer, c schema.Cell, r render.RenderCellFunc) error { return r(w, c) }
func (*fakeWrapper) WrapInput(w io.Writer, c schema.Cell, r render.RenderCellFunc) error {
return r(w, c)
Expand Down
4 changes: 2 additions & 2 deletions render/html/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ type Config struct {

type Option func(*Config)

// WithCSSWriter
// WithCSSWriter registers a writer for CSS stylesheet.
func WithCSSWriter(w io.Writer) Option {
return func(c *Config) {
c.CSSWriter = &WriterOnce{w: w}
c.CSSWriter = w
}
}

Expand Down
23 changes: 8 additions & 15 deletions render/html/html_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ func TestRenderer(t *testing.T) {
"src": {"data:image/jpeg;base64, base64-encoded-image"},
}},
},
{
name: "image/svg+xml",
cell: test.DisplayData("svg-image", "image/svg+xml"),
want: &node{tag: "img", attr: map[string][]string{
"src": {"data:image/svg+xml;base64, svg-image"},
}},
},
{
name: "code cell",
cell: &test.CodeCell{
Expand Down Expand Up @@ -108,20 +115,6 @@ func TestRenderer(t *testing.T) {
}

func TestRenderer_CSSWriter(t *testing.T) {
t.Run("WithCSSWriter wraps in WriterOnce", func(t *testing.T) {
// Arrange
var cfg html.Config
opt := html.WithCSSWriter(io.Discard)

// Act
opt(&cfg)

// Assert
if _, ok := cfg.CSSWriter.(*html.WriterOnce); !ok {
t.Errorf("expected *html.WriterOnce, got %T", cfg.CSSWriter)
}
})

t.Run("captures correct css", func(t *testing.T) {
// Arrange
var css bytes.Buffer
Expand All @@ -134,7 +127,7 @@ func TestRenderer_CSSWriter(t *testing.T) {
}

// Act
err = r.Wrap(io.Discard, test.Markdown(""), noopRender)
err = r.WrapAll(io.Discard, func(w io.Writer) error { return nil })
require.NoError(t, err)

// Assert
Expand Down
30 changes: 10 additions & 20 deletions render/html/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"io"
"sort"
"strings"
"sync"

"github.com/bevzzz/nb/render"
"github.com/bevzzz/nb/schema"
Expand All @@ -15,21 +14,28 @@ import (

/*
TODO:
- make class prefixes configurable (probably on the html.Renderer level).
- make class prefixes configurable (probably on the html.Config level).
- refactor to use tagger
- add WrapAll / WrapNotebook that would add a <div class="jp-Notebook"> (then there's no need for WriterOnce)
*/

// Wrapper wraps cells in the HTML produced by the original Jupyter's nbconvert.
type Wrapper struct {
Config
}

func (wr *Wrapper) Wrap(w io.Writer, cell schema.Cell, render render.RenderCellFunc) error {
var _ render.CellWrapper = (*Wrapper)(nil)

func (wr *Wrapper) WrapAll(w io.Writer, render func(io.Writer) error) error {
if wr.CSSWriter != nil {
wr.CSSWriter.Write(jupyterCSS)
}

div.Open(w, attributes{"class": {"jp-Notebook"}}, true)
defer div.Close(w)
return render(w)
}

func (wr *Wrapper) Wrap(w io.Writer, cell schema.Cell, render render.RenderCellFunc) error {
var ct string
switch cell.Type() {
case schema.Markdown:
Expand Down Expand Up @@ -301,19 +307,3 @@ func (attrs attributes) WriteTo(w io.Writer) (n64 int64, err error) {
}
return
}

// WriterOnce writes to the writer once only.
// TODO: move to util
type WriterOnce struct {
w io.Writer
once sync.Once
}

var _ io.Writer = (*WriterOnce)(nil)

func (w *WriterOnce) Write(p []byte) (n int, err error) {
w.once.Do(func() {
n, err = w.w.Write(p)
})
return
}
16 changes: 16 additions & 0 deletions render/html/wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ import (

func noopRender(w io.Writer, c schema.Cell) error { return nil }

func TestWrapper_WrapAll(t *testing.T) {
// Arrange
var w html.Wrapper
var buf bytes.Buffer
want := node{tag: "div", attr: map[string][]string{
"class": {"jp-Notebook"},
}}

// Act
err := w.WrapAll(&buf, func(w io.Writer) error { return nil })
require.NoError(t, err)

// Assert
checkDOM(t, &buf, &want)
}

func TestWrapper_Wrap(t *testing.T) {
for _, tt := range []struct {
name string
Expand Down
11 changes: 8 additions & 3 deletions render/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,19 @@ func WithCellRenderers(crs ...CellRenderer) Option {

// CellWrapper renders common wrapping elements for every cell type.
type CellWrapper interface {
// Wrap the entire cell block.
// Wrap the entire cell.
Wrap(io.Writer, schema.Cell, RenderCellFunc) error

// Wrap input block.
// WrapInput wraps input block.
WrapInput(io.Writer, schema.Cell, RenderCellFunc) error

// Wrap output block (code cells).
// WrapOuput wraps output block (code cells).
WrapOutput(io.Writer, schema.Outputter, RenderCellFunc) error

// WrapAll wraps all cells in the notebook.
// This method will be called once and will receive a function
// to render the rest of the notebook.
WrapAll(io.Writer, func(io.Writer) error) error
}

// renderer is a base Renderer implementation.
Expand Down

0 comments on commit b51732d

Please sign in to comment.