Repository: holoviz/panel

Issue:
Numeric Param Widgets, initialised with None, displayed as active "0"

#### ALL software version info
Panel 10.1

#### Description of expected behavior and the observed behavior
Widgets or parameters initialised as "None" has value of 'None', but still display an active '0' 

#### Complete, minimal, self-contained example code that reproduces the issue

```
class Test(param.Parameterized) :
    w_none_but_0 = param.Number(default=None, allow_None=True)
    w_none_forced = param.Number(default=None, allow_None=True)

    
    # Since default None, was not displayed correctly, tried to override with None,
    # to clear the widget, but had no effect..
    def __init__(self, **params):
        super().__init__(**params)
        self.param.set_param("w_none_forced", 450) 
        self.param.set_param("w_none_forced", None)    # didn't improve anything
          
    
t = Test()
pn.Row(t)

```

#### Screenshots or screencasts of the bug in action
![image](https://user-images.githubusercontent.com/3274852/102081667-82336980-3e08-11eb-8fa9-c13836f30b91.png)




Discussion:

Resolving patch:
diff --git a/panel/param.py b/panel/param.py
--- a/panel/param.py
+++ b/panel/param.py
@@ -422,7 +422,10 @@ def widget(self, p_name):
             kw['visible'] = not p_obj.constant
 
         value = getattr(self.object, p_name)
-        if value is not None:
+        allow_None = p_obj.allow_None or False
+        if isinstance(widget_class, type) and issubclass(widget_class, Widget):
+            allow_None &= widget_class.param.value.allow_None
+        if value is not None or allow_None:
             kw['value'] = value
 
         if hasattr(p_obj, 'get_range'):
diff --git a/panel/widgets/input.py b/panel/widgets/input.py
--- a/panel/widgets/input.py
+++ b/panel/widgets/input.py
@@ -524,7 +524,7 @@ class _SpinnerBase(_NumericInputBase):
     __abstract = True
 
     def __init__(self, **params):
-        if params.get('value') is None:
+        if 'value' not in params:
             value = params.get('start', self.value)
             if value is not None:
                 params['value'] = value
@@ -638,6 +638,9 @@ class LiteralInput(Widget):
     >>> LiteralInput(name='Dictionary', value={'key': [1, 2, 3]}, type=dict)
     """
 
+    placeholder = param.String(default='', doc="""
+      Placeholder for empty input field.""")
+
     serializer = param.ObjectSelector(default='ast', objects=['ast', 'json'], doc="""
        The serialization (and deserialization) method to use. 'ast'
        uses ast.literal_eval and 'json' uses json.loads and json.dumps.
@@ -685,7 +688,9 @@ def _process_property_change(self, msg):
         if 'value' in msg:
             value = msg.pop('value')
             try:
-                if self.serializer == 'json':
+                if value == '':
+                    value = ''
+                elif self.serializer == 'json':
                     value = json.loads(value)
                 else:
                     value = ast.literal_eval(value)
@@ -703,7 +708,9 @@ def _process_property_change(self, msg):
                             pass
                         else:
                             break
-                    if typed_value is None and value is not None:
+                    if typed_value is None and value == '':
+                        value = None
+                    elif typed_value is None and value is not None:
                         new_state = ' (wrong type)'
                         value = self.value
                     else:
diff --git a/panel/widgets/slider.py b/panel/widgets/slider.py
--- a/panel/widgets/slider.py
+++ b/panel/widgets/slider.py
@@ -156,21 +156,19 @@ class FloatSlider(ContinuousSlider):
     >>> FloatSlider(value=0.5, start=0.0, end=1.0, step=0.1, name="Float value")
     """
 
-    value = param.Number(default=0.0, doc="""
-        The selected floating-point value of the slider. Updated when the handle is dragged.
-        """)
+    start = param.Number(default=0.0, doc="The lower bound.")
 
-    value_throttled = param.Number(default=None, constant=True, doc="""
-         The value of the slider. Updated when the handle is released.""")
+    end = param.Number(default=1.0, doc="The upper bound.")
 
-    start = param.Number(default=0.0, doc="""
-        The lower bound.""")
+    step = param.Number(default=0.1, doc="The step size.")
 
-    end = param.Number(default=1.0, doc="""
-        The upper bound.""")
+    value = param.Number(default=0.0, allow_None=True, doc="""
+        The selected floating-point value of the slider. Updated when
+        the handle is dragged."""
+    )
 
-    step = param.Number(default=0.1, doc="""
-        The step size.""")
+    value_throttled = param.Number(default=None, constant=True, doc="""
+         The value of the slider. Updated when the handle is released.""")
 
     _rename: ClassVar[Mapping[str, str | None]] = {'name': 'title'}
 
@@ -187,9 +185,6 @@ class IntSlider(ContinuousSlider):
     >>> IntSlider(value=5, start=0, end=10, step=1, name="Integer Value")
     """
 
-    value = param.Integer(default=0, doc="""
-        The selected integer value of the slider. Updated when the handle is dragged.""")
-
     start = param.Integer(default=0, doc="""
         The lower bound.""")
 
@@ -199,6 +194,9 @@ class IntSlider(ContinuousSlider):
     step = param.Integer(default=1, doc="""
         The step size.""")
 
+    value = param.Integer(default=0, allow_None=True, doc="""
+        The selected integer value of the slider. Updated when the handle is dragged.""")
+
     value_throttled = param.Integer(default=None, constant=True, doc="""
         The value of the slider. Updated when the handle is released""")
 
@@ -500,7 +498,7 @@ def values(self):
 
 class _RangeSliderBase(_SliderBase):
 
-    value = param.Tuple(length=2, doc="""
+    value = param.Tuple(length=2, allow_None=False, doc="""
         The selected range of the slider. Updated when a handle is dragged.""")
 
     value_start = param.Parameter(readonly=True, doc="""The lower value of the selected range.""")
@@ -511,9 +509,11 @@ class _RangeSliderBase(_SliderBase):
 
     def __init__(self, **params):
         if 'value' not in params:
-            params['value'] = (params.get('start', self.start),
-                               params.get('end', self.end))
-        params['value_start'], params['value_end'] = params['value']
+            params['value'] = (
+                params.get('start', self.start), params.get('end', self.end)
+            )
+        if params['value'] is not None:
+            params['value_start'], params['value_end'] = params['value']
         with edit_readonly(self):
             super().__init__(**params)
 
@@ -546,7 +546,7 @@ class RangeSlider(_RangeSliderBase):
     ... )
     """
 
-    value = param.Range(default=(0, 1), doc=
+    value = param.Range(default=(0, 1), allow_None=False, doc=
         """The selected range as a tuple of values. Updated when a handle is
         dragged.""")
 
@@ -638,7 +638,7 @@ class DateRangeSlider(_SliderBase):
     ... )
     """
 
-    value = param.DateRange(default=None, doc="""
+    value = param.DateRange(default=None, allow_None=False, doc="""
         The selected range as a tuple of values. Updated when one of the handles is
         dragged. Supports datetime.datetime, datetime.date, and np.datetime64 ranges.""")
 
@@ -934,7 +934,8 @@ class EditableRangeSlider(CompositeWidget, _SliderBase):
     ... )
     """
 
-    value = param.Range(default=(0, 1), doc="Current range value. Updated when a handle is dragged")
+    value = param.Range(default=(0, 1), allow_None=False, doc="""
+        Current range value. Updated when a handle is dragged.""")
 
     value_throttled = param.Range(default=None, constant=True, doc="""
         The value of the slider. Updated when the handle is released.""")


Test patch:
diff --git a/panel/tests/test_param.py b/panel/tests/test_param.py
--- a/panel/tests/test_param.py
+++ b/panel/tests/test_param.py
@@ -419,6 +419,16 @@ class Test(param.Parameterized):
     assert select.disabled == False
 
 
+def test_number_input_none_support():
+    class Test(param.Parameterized) :
+        number = param.Number(default=0, allow_None=True)
+        none = param.Number(default=None, allow_None=True)
+
+    test_widget = Param(Test())
+    assert test_widget[1].value == 0
+    assert test_widget[2].value is None
+
+
 def test_explicit_params(document, comm):
     class Test(param.Parameterized):
         a = param.Boolean(default=False)