How to set a custom widget property of type "Dict"?

I’m trying to add a property to my custom widget that accepts a dictionary.
The dictionary is expected to include css rules in the following structure:

{
  <selector>: {<property>: <value>},
  ...
}

On the python side I defined the property like this:

custom_styles = Dict(String, Dict(String, Either(String, Either(Float, Int))), default=None, help=""""
Additional CSS rules.
""")

On the typescript side:

custom_styles: p.Property<{[key: string]: any} | null>

and

custom_styles:  [ Nullable(Dict(String, Dict(String, Or(String, Number)))), null ],

When loading the widget, I get the following error in the console:

The invalid value that’s specified in the error is the value that I set for the property.
Any idea what’s wrong here?

@roinr That seems fine, offhand. What happens if you loosen the property type to Dict(String, Any) everywhere, is there still a validation error?

cc @mateusz

yeah…same error.

OS: WSL
Bokeh: v2.2.3
Python: v3.7.10

Can you provide a Minimal Reproducible Example? I can’t really speculate further without running real code.

@Bryan This should work:

from bokeh.models.widgets.markups import Markup
from bokeh.core.properties import String, Dict, AnyRef
from bokeh.util.compiler import TypeScript
from bokeh.io import curdoc

TS_CODE = """
import {Markup, MarkupView} from "models/widgets/markup"
import * as p from "core/properties"

export class CustomDivView extends MarkupView {
  model: CustomDiv

  render(): void {
    super.render()
    if (this.model.render_as_text)
      this.markup_el.textContent = this.model.text
    else
      this.markup_el.innerHTML = this.model.text
  }
}

export namespace CustomDiv {
  export type Attrs = p.AttrsOf<Props>

  export type Props = Markup.Props & {
    render_as_text: p.Property<boolean>
    custom_styles: p.Property<({[key: string]: any} | null)>
  }
}

export interface CustomDiv extends CustomDiv.Attrs {}

export class CustomDiv extends Markup {
  properties: CustomDiv.Props
  __view_type__: CustomDivView

  constructor(attrs?: Partial<CustomDiv.Attrs>) {
    super(attrs)
  }

  static init_CustomDiv(): void {
    this.prototype.default_view = CustomDivView

    this.define<CustomDiv.Props>(({Boolean, Nullable, Dict, String, Any}) => ({
      render_as_text: [ Boolean, false ],
      custom_styles: [ Nullable(Dict(String, Any)), null ],
    }))
  }
}
"""


class CustomDiv(Markup):
    ''' Custom Div widget.
    '''
    __implementation__ = TypeScript(TS_CODE)

    custom_styles = Dict(String, AnyRef, default=None, help=""""
    Additional CSS rules.
    """)


div = CustomDiv(
    text="Hello world!",
    custom_styles={"div.bk.bk-clearfix": {"font-size": "30px"}}
)

curdoc().add_root(div)

@roinr The only way I could make it work was with

      custom_styles: [ Nullable(Any), null ],

Note your code above was also missing Nullable on the Python side. I’d suggest filing a GitHub Issue with the MRE.