Browse Source

Merge branch 'main' of github.com:zauberzeug/nicegui

Falko Schindler 2 years ago
parent
commit
e4eface248
3 changed files with 43 additions and 2 deletions
  1. 3 1
      CONTRIBUTING.md
  2. 27 1
      nicegui/elements/number.py
  3. 13 0
      tests/test_number.py

+ 3 - 1
CONTRIBUTING.md

@@ -133,7 +133,9 @@ We are happy to merge pull requests with new examples which show new concepts, i
 
 
 ## Pull requests
 ## Pull requests
 
 
-To get started, fork the repository on GitHub, make your changes, and open a pull request (PR) with a detailed description of the changes you've made.
+To get started, fork the repository on GitHub, clone it somewhere on your filesystem, commit and push your changes,
+and then open a pull request (PR) with a detailed description of the changes you've made
+(the PR button is shown on the GitHub website of your forked repository).
 
 
 When submitting a PR, please make sure that the code follows the existing coding style and that all tests are passing.
 When submitting a PR, please make sure that the code follows the existing coding style and that all tests are passing.
 If you're adding a new feature, please include tests that cover the new functionality.
 If you're adding a new feature, please include tests that cover the new functionality.

+ 27 - 1
nicegui/elements/number.py

@@ -10,6 +10,11 @@ class Number(ValueElement):
                  label: Optional[str] = None, *,
                  label: Optional[str] = None, *,
                  placeholder: Optional[str] = None,
                  placeholder: Optional[str] = None,
                  value: Optional[float] = None,
                  value: Optional[float] = None,
+                 min: Optional[float] = None,
+                 max: Optional[float] = None,
+                 step: Optional[float] = None,
+                 prefix: Optional[str] = None,
+                 suffix: Optional[str] = None,
                  format: Optional[str] = None,
                  format: Optional[str] = None,
                  on_change: Optional[Callable] = None,
                  on_change: Optional[Callable] = None,
                  validation: Dict[str, Callable] = {}) -> None:
                  validation: Dict[str, Callable] = {}) -> None:
@@ -23,6 +28,11 @@ class Number(ValueElement):
         :param label: displayed name for the number input
         :param label: displayed name for the number input
         :param placeholder: text to show if no value is entered
         :param placeholder: text to show if no value is entered
         :param value: the initial value of the field
         :param value: the initial value of the field
+        :param min: the minimum value allowed
+        :param max: the maximum value allowed
+        :param step: the step size for the stepper buttons
+        :param prefix: a prefix to prepend to the displayed value
+        :param suffix: a suffix to append to the displayed value
         :param format: a string like "%.2f" to format the displayed value
         :param format: a string like "%.2f" to format the displayed value
         :param on_change: callback to execute when the input is confirmed by leaving the focus
         :param on_change: callback to execute when the input is confirmed by leaving the focus
         :param validation: dictionary of validation rules, e.g. ``{'Too small!': lambda value: value < 3}``
         :param validation: dictionary of validation rules, e.g. ``{'Too small!': lambda value: value < 3}``
@@ -34,8 +44,24 @@ class Number(ValueElement):
             self._props['label'] = label
             self._props['label'] = label
         if placeholder is not None:
         if placeholder is not None:
             self._props['placeholder'] = placeholder
             self._props['placeholder'] = placeholder
+        if min is not None:
+            self._props['min'] = min
+        if max is not None:
+            self._props['max'] = max
+        if step is not None:
+            self._props['step'] = step
+        if prefix is not None:
+            self._props['prefix'] = prefix
+        if suffix is not None:
+            self._props['suffix'] = suffix
         self.validation = validation
         self.validation = validation
-        self.on('blur', self.update)  # NOTE: to apply format (#736)
+        self.on('blur', self.sanitize)
+
+    def sanitize(self) -> None:
+        value = float(self.value or 0)
+        value = max(value, self._props.get('min', -float('inf')))
+        value = min(value, self._props.get('max', float('inf')))
+        self.set_value(self.format % value if self.format else str(value))
 
 
     def on_value_change(self, value: Any) -> None:
     def on_value_change(self, value: Any) -> None:
         super().on_value_change(value)
         super().on_value_change(value)

+ 13 - 0
tests/test_number.py

@@ -16,3 +16,16 @@ def test_apply_format_on_blur(screen: Screen):
     element.send_keys('789')
     element.send_keys('789')
     screen.click('Button')
     screen.click('Button')
     screen.should_contain_input('3.1417')
     screen.should_contain_input('3.1417')
+
+
+def test_max_value(screen: Screen):
+    ui.number('Number', min=0, max=10, value=5)
+    ui.button('Button')
+
+    screen.open('/')
+    screen.should_contain_input('5')
+
+    element = screen.selenium.find_element(By.XPATH, '//*[@aria-label="Number"]')
+    element.send_keys('6')
+    screen.click('Button')
+    screen.should_contain_input('10')