Skip to content

Commit

Permalink
v0.1.18 submit function; ci jobs (#71)
Browse files Browse the repository at this point in the history
* User can specify a submit_function that runs after submitting (#69)

* User can specify a function that runs after submitting a new value from the combobox

* Added a new parameter: submit_function: Callable[[str], None] | None = None
* The method in submit_function runs once after a new selection from the options menu is made.
* The option value is passed to the method.

* Add submit function in the readme

* fix pre-commit; remove 3.8

* bump versions;

* update examples; add highlighted option;

* changelog;

* fix readme highlights

* update pre-commit;

* fix playwright;

* remove skip;

* update ci scope;


---------

Co-authored-by: bram49 <[email protected]>
  • Loading branch information
m-wrzr and bram49 authored Nov 1, 2024
1 parent f05ab35 commit b12f32d
Show file tree
Hide file tree
Showing 17 changed files with 289 additions and 139 deletions.
24 changes: 19 additions & 5 deletions .github/actions/setup_backend/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,40 @@ inputs:
python-version:
description: 'Python version to use'
required: true
default: '3.11'
default: '3.12'
streamlit-version:
description: 'Streamlit version to use'
required: true
default: '1.31'
default: '1.36'

runs:
using: composite
steps:

- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}

- name: Cache Python dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('setup.py') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Display Python version
shell: bash --login -eo pipefail {0}
run: python -c "import sys; print(sys.version)"
shell: bash --login -eo pipefail {0}

- run: pip install streamlit==${{ inputs.streamlit-version }}
- name: Install Streamlit
run: |
if [[ ${{ inputs.streamlit-version }} == 'latest' ]]; then
pip install streamlit
else
pip install streamlit==${{ inputs.streamlit-version }}
fi
shell: bash --login -eo pipefail {0}

- run: pip install -e ".[dev,tests]"
Expand Down
4 changes: 2 additions & 2 deletions .github/actions/setup_frontend/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ runs:
steps:

- name: Use Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 16
node-version: 20
cache: 'npm'
cache-dependency-path: streamlit_searchbox/frontend/package-lock.json

Expand Down
15 changes: 11 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.10", "3.12"]
python-version: ["3.9", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand All @@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.10", "3.12"]
python-version: ["3.9", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand All @@ -37,8 +37,8 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.10", "3.12"]
streamlit-version: ["1.25", "1.30", "1.31", "1.36"]
python-version: ["3.9", "3.12"]
streamlit-version: ["1.25", "1.36", "latest"]

steps:
- uses: actions/checkout@v4
Expand All @@ -51,4 +51,11 @@ jobs:
- run: playwright install

- run: python -m pytest tests/

# run playwright tests & save screenshots
- run: python -m pytest tests/playwright.py --verbose

- uses: actions/upload-artifact@v4
with:
name: screenshots-${{ matrix.python-version }}-${{ matrix.streamlit-version }}
path: tests/screenshots/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ README.local.md
.streamlit
venv*
tests/screenshots/*.png
tests_local
31 changes: 14 additions & 17 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.12.0
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.291'
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.1
hooks:
- id: ruff
- id: ruff
name: lint with ruff
- id: ruff
name: sort imports with ruff
args: [--select, I, --fix]
- id: ruff-format
name: format with ruff
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to this project will be documented in this file.

## [0.1.18] - 2024-10-28

- added `submit_function` that is called on a new submission
- added `highlighColor` for option styling that highlights matching text
- removed python `3.8` from ci

## [0.1.17] - 2024-09-15

- `clear_on_submit` now also resets the `default_options`
Expand Down
32 changes: 26 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,25 @@ Create a searchbox component and pass a `search_function` that accepts a `str` s
You can either pass a list of arguments, e.g.

```python
import streamlit as st
import wikipedia

from streamlit_searchbox import st_searchbox

# function with list of labels
def search_wikipedia(searchterm: str) -> List[any]:

def search_wikipedia(searchterm: str) -> list:
# search wikipedia for the searchterm
return wikipedia.search(searchterm) if searchterm else []


# pass search function to searchbox
# pass search function and other options as needed
selected_value = st_searchbox(
search_wikipedia,
key="wiki_searchbox",
placeholder="Search Wikipedia... ",
key="my_key",
)

st.write(f"Selected value: {selected_value}")
```

This example will call the Wikipedia Api to reload suggestions. The `selected_value` will be one of the items the `search_wikipedia` function returns, the suggestions shown in the UI components are a `str` representation. In case you want to provide custom text for suggestions, pass a `Tuple`.
Expand Down Expand Up @@ -163,6 +169,12 @@ reset_function: Callable[[], None] | None = None

Function that will be called when the combobox is reset.

```python
submit_function: Callable[[Any], None] | None = None
```

Function that will be called when a new option is selected, with the selected option as argument.

---

### Custom Styles
Expand All @@ -187,20 +199,25 @@ An example Streamlit app can be found [here](./example.py)

To further customize the styling of the searchbox, you can override the default styling by passing `style_overrides` which will be directly applied in the react components. See below for an example, for more information on the available attributes, please see [styling.tsx](./streamlit_searchbox/frontend/src/styling.tsx) as well as the [react-select](https://react-select.com/styles) documentation.

```json
```javascript
{
// change the clear icon
"clear":{
"width":20,
"height":20,
// also available: circle-unfilled, circle-filled
"icon":"cross",
// also available: never, after-submit
"clearable":"always"
},
// change the dropdown icon
"dropdown":{
"rotate":true,
"width":30,
"height":30,
"fill":"red"
},
// styling for the searchbox itself, mostly passed to react-select
"searchbox":{
"menuList":{
"backgroundColor":"transparent"
Expand All @@ -210,7 +227,9 @@ To further customize the styling of the searchbox, you can override the default
},
"option":{
"color":"blue",
"backgroundColor":"yellow"
"backgroundColor":"yellow",
// highlight matching text
"highlightColor":"green"
}
}
}
Expand All @@ -237,3 +256,4 @@ We welcome contributions from everyone. Here are a few ways you can help:
- [@Jumitti](https://github.com/Jumitti) `st.rerun` compatibility
- [@salmanrazzaq-94](https://github.com/salmanrazzaq-94) `st.fragment` support
- [@hoggatt](https://github.com/hoggatt) `reset_function`
- [@bram49](https://github.com/bram49) `submit_function`
11 changes: 9 additions & 2 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ def search_kwargs(searchterm: str, **kwargs) -> List[str]:
default_options=["inital", "list", "of", "options"],
key=f"{search.__name__}_default_options",
label=f"{search.__name__}_default_options",
style_overrides={"clear": {"width": 25, "height": 25}},
style_overrides={
"clear": {"width": 25, "height": 25},
"searchbox": {"option": {"highlight": "#f1660f"}},
},
),
dict(
search_function=search,
Expand Down Expand Up @@ -268,7 +271,11 @@ def search_kwargs(searchterm: str, **kwargs) -> List[str]:
"searchbox": {
"menuList": {"backgroundColor": "transparent"},
"singleValue": {"color": "red", "some": "data"},
"option": {"color": "blue", "backgroundColor": "yellow"},
"option": {
"color": "blue",
"backgroundColor": "yellow",
"highlightColor": "green",
},
},
},
)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ exclude = [
line-length = 88
target-version = "py311"

[tool.ruff.lint]
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

Expand Down
20 changes: 10 additions & 10 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setuptools.setup(
name="streamlit-searchbox",
version="0.1.17",
version="0.1.18",
author="m-wrzr",
description="Autocomplete Searchbox",
long_description="Streamlit searchbox that dynamically updates "
Expand All @@ -12,24 +12,24 @@
packages=setuptools.find_packages(),
include_package_data=True,
classifiers=[],
python_requires=">=3.8, !=3.9.7",
python_requires=">=3.9, !=3.9.7",
install_requires=[
# version >1.37 reruns can lead to constant iFrame resets
# version 1.35/1.36 also have reset issues but less frequent
"streamlit >= 1.0",
],
extras_require={
"tests": [
"wikipedia",
"pytest",
"pytest-playwright",
"wikipedia==1.4.0",
"pytest==8.3.2",
# NOTE: run `playwright install` to install the browser drivers
"playwright==1.46.0",
"pytest-playwright==0.5.1",
],
"dev": [
"pre-commit",
"black",
"isort",
"ruff",
"pyright",
"pre-commit==4.0.1",
"ruff==0.7.1",
"pyright==1.1.377",
],
},
)
26 changes: 23 additions & 3 deletions streamlit_searchbox/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def _list_to_options_py(options: list[Any] | list[tuple[str, Any]]) -> list[Any]


def _list_to_options_js(
options: list[Any] | list[tuple[str, Any]]
options: list[Any] | list[tuple[str, Any]],
) -> list[dict[str, Any]]:
"""
unpack search options for use in react component
Expand Down Expand Up @@ -175,14 +175,24 @@ def _set_defaults(
total=False,
)

OptionStyle = TypedDict(
"OptionStyle",
{
"color": str,
"backgroundColor": str,
"highlightColor": str,
},
total=False,
)


class SearchboxStyle(TypedDict, total=False):
menuList: dict | None
singleValue: dict | None
input: dict | None
placeholder: dict | None
control: dict | None
option: dict | None
option: OptionStyle | None


class StyleOverrides(TypedDict, total=False):
Expand All @@ -209,6 +219,7 @@ def st_searchbox(
debounce: int = 150,
min_execution_time: int = MIN_EXECUTION_TIME_DEFAULT,
reset_function: Callable[[], None] | None = None,
submit_function: Callable[[Any], None] | None = None,
key: str = "searchbox",
rerun_scope: Literal["app", "fragment"] = "app",
**kwargs,
Expand Down Expand Up @@ -253,6 +264,9 @@ def st_searchbox(
within the component in some streamlit versions. Defaults to 0.
reset_function (Callable[[], None], optional):
Function that is called after the user reset the combobox. Defaults to None.
submit_function (Callable[[any], None], optional):
Function that is called after the user submits a new/unique option from the
combobox. Defaults to None.
key (str, optional):
Streamlit session key. Defaults to "searchbox".
Expand Down Expand Up @@ -310,12 +324,18 @@ def st_searchbox(
)

if interaction == "submit":
st.session_state[key]["result"] = (
submit_value = (
st.session_state[key]["options_py"][value]
if "options_py" in st.session_state[key]
else value
)

# ensure submit_function only runs when value changed
if st.session_state[key]["result"] != submit_value:
st.session_state[key]["result"] = submit_value
if submit_function is not None:
submit_function(submit_value)

if clear_on_submit:
_set_defaults(key, st.session_state[key]["result"], default_options)
_rerun(rerun_scope)
Expand Down
Loading

0 comments on commit b12f32d

Please sign in to comment.