Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
c176d02
Checkbox checkpoint. Still broken.
sfc-gh-tteixeira Aug 23, 2024
7f22b28
Update Checkbox widget to functional component.
sfc-gh-tteixeira Aug 24, 2024
231bb65
Introduce useBasicWidgetState and convert Checkbox and Radio to it.
sfc-gh-tteixeira Aug 28, 2024
8f8fbfc
Make useFormClearHelper follow latest pattern
sfc-gh-tteixeira Aug 28, 2024
5c46436
Merge remote-tracking branch 'origin/develop' into checkbox
sfc-gh-tteixeira Aug 28, 2024
8d414ad
Switching some widgets to use FormClearHelper (need to fix NumberInpu…
sfc-gh-tteixeira Sep 5, 2024
28fa8e3
Fix tests that required async/await
sfc-gh-tteixeira Sep 5, 2024
5414bb1
Address code review comments, lint, etc.
sfc-gh-tteixeira Sep 6, 2024
928f245
Forgot to rename props type
sfc-gh-tteixeira Sep 6, 2024
023371c
Make useFormClearHelper() use WidgetStateManager directly rather than…
sfc-gh-tteixeira Sep 6, 2024
cbad1ae
Merge remote-tracking branch 'origin/develop' into checkbox
sfc-gh-tteixeira Sep 6, 2024
3c98c61
Merge branch 'checkbox' into formcleaner
sfc-gh-tteixeira Sep 6, 2024
577cd20
Also convert underlying /shared/Radio widget to a function component.
sfc-gh-tteixeira Sep 6, 2024
5f81fa0
Merge branch 'checkbox' into formcleaner
sfc-gh-tteixeira Sep 6, 2024
f55b686
Fix radio test
sfc-gh-tteixeira Sep 6, 2024
b3f8176
Fix NumberInput
sfc-gh-tteixeira Sep 6, 2024
a5e898c
Merge branch 'checkbox' into formcleaner
sfc-gh-tteixeira Sep 6, 2024
02bf2d4
Merge remote-tracking branch 'origin/develop' into formcleaner
sfc-gh-tteixeira Sep 12, 2024
93aa3c9
Fix types for useBasicWidgetState
sfc-gh-tteixeira Sep 14, 2024
0963004
Make TimeInput functional
sfc-gh-tteixeira Sep 14, 2024
33b0eeb
Make DateInput functional
sfc-gh-tteixeira Sep 14, 2024
fca1fe0
Make ColorPicker functional
sfc-gh-tteixeira Sep 14, 2024
017ccc3
Make Selectbox functional
sfc-gh-tteixeira Sep 14, 2024
10ce681
Add tests
sfc-gh-tteixeira Sep 15, 2024
f1b9211
Make Slider functional
sfc-gh-tteixeira Sep 15, 2024
fb2bd11
Make TextInput functional
sfc-gh-tteixeira Sep 15, 2024
c6e614c
Merge remote-tracking branch 'origin/develop' into formcleaner
sfc-gh-tteixeira Sep 15, 2024
dbfcf01
Fix ColorPicker tests
sfc-gh-tteixeira Sep 15, 2024
add90bc
Remove unused state interface
sfc-gh-tteixeira Sep 15, 2024
8bba4db
Make TextArea functional
sfc-gh-tteixeira Sep 15, 2024
e5f1998
Fix slider focus state
sfc-gh-tteixeira Sep 17, 2024
d73e215
Fix tests
sfc-gh-tteixeira Sep 17, 2024
7033889
Rename ValueWSource to ValueWithSource
sfc-gh-tteixeira Sep 17, 2024
88d540e
Merge remote-tracking branch 'origin/develop' into formcleaner
sfc-gh-tteixeira Sep 17, 2024
1c0e997
Lint lint lint lint lint
sfc-gh-tteixeira Sep 17, 2024
127bb44
Merge remote-tracking branch 'origin/develop' into functionalcomponents
sfc-gh-tteixeira Sep 17, 2024
c56cd0f
Merge remote-tracking branch 'origin/develop' into functionalcomponents
sfc-gh-tteixeira Oct 4, 2024
819d821
Address some comments and lint
sfc-gh-tteixeira Oct 4, 2024
7f3a5e6
Remove async/await in act() when not necessary.
sfc-gh-tteixeira Oct 4, 2024
99a8e84
Merge remote-tracking branch 'origin/develop' into functionalcomponents
sfc-gh-tteixeira Oct 6, 2024
85f381f
Merge remote-tracking branch 'origin/develop' into functionalcomponents
sfc-gh-tteixeira Oct 15, 2024
e033c8c
Fix slider value positioning algorithm.
sfc-gh-tteixeira Oct 19, 2024
9259cfe
Merge remote-tracking branch 'origin/develop' into functionalcomponents
sfc-gh-tteixeira Oct 19, 2024
5dda34a
Merge
sfc-gh-tteixeira Oct 19, 2024
52071a7
Lint
sfc-gh-tteixeira Oct 19, 2024
8211aad
Update frontend/lib/src/components/widgets/Slider/Slider.tsx
sfc-gh-tteixeira Oct 21, 2024
42bec76
Update frontend/lib/src/components/widgets/Slider/Slider.tsx
sfc-gh-tteixeira Oct 21, 2024
61c05c6
Update frontend/lib/src/components/widgets/Slider/Slider.tsx
sfc-gh-tteixeira Oct 21, 2024
cc9916b
Merge remote-tracking branch 'origin/develop' into functionalcomponents
sfc-gh-tteixeira Oct 22, 2024
5eee85c
Add tests for slider overlaps
sfc-gh-tteixeira Oct 22, 2024
201f62e
Merge remote-tracking branch 'origin/functionalcomponents' into funct…
sfc-gh-tteixeira Oct 22, 2024
01dddaf
Address comments and fix test
sfc-gh-tteixeira Oct 22, 2024
7eee80d
Merge branch 'develop' into functionalcomponents
raethlein Oct 25, 2024
20ccfdc
Fix import issue
raethlein Oct 25, 2024
5b4a4c8
Update snapshots
raethlein Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions e2e_playwright/st_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,24 @@
import streamlit as st
from streamlit import runtime

# Slider 0
s1 = st.sidebar.slider("Label A", 0, 12345678, 12345678)
st.sidebar.write("Value A:", s1)

# Slider 1
r1 = st.sidebar.slider("Range A", 10000, 25000, [10000, 25000])
st.sidebar.write("Range Value A:", r1)

with st.sidebar.expander("Expander", expanded=True):
# Slider 2
s2 = st.slider("Label B", 10000, 25000, 10000)
st.write("Value B:", s2)

# Slider 3
r2 = st.slider("Range B", 10000, 25000, [10000, 25000])
st.write("Range Value B:", r2)

# Slider 4
w1 = st.slider(
"Label 1",
min_value=date(2019, 8, 1),
Expand All @@ -40,9 +45,11 @@
)
st.write("Value 1:", w1)

# Slider 5
w2 = st.slider("Label 2", 0.0, 100.0, (25.0, 75.0), 0.5)
st.write("Value 2:", w2)

# Slider 6
w3 = st.slider(
"Label 3 - This is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long label",
0,
Expand All @@ -52,15 +59,19 @@
)
st.write("Value 3:", w3)

# Slider 7
w4 = st.slider("Label 4", 10000, 25000, 10000, disabled=True)
st.write("Value 4:", w4)

# Slider 8
w5 = st.slider("Label 5", 0, 100, 25, 1, label_visibility="hidden")
st.write("Value 5:", w5)

# Slider 9
w6 = st.slider("Label 6", 0, 100, 36, label_visibility="collapsed")
st.write("Value 6:", w6)

# Slider 10
dates = st.slider(
"Label 7",
min_value=date(2019, 8, 1),
Expand All @@ -74,6 +85,7 @@
def on_change():
st.session_state.slider_changed = True

# Slider 11
st.slider(
"Label 8",
min_value=0,
Expand All @@ -87,6 +99,7 @@ def on_change():
st.write("Slider changed:", "slider_changed" in st.session_state)

with st.form(key="my_form", clear_on_submit=True):
# Slider 12
selection = st.slider(
"Label 9",
min_value=0,
Expand All @@ -102,6 +115,7 @@ def on_change():

@st.fragment
def test_fragment():
# Slider 13
selection = st.slider(
"Label 10",
min_value=0,
Expand All @@ -121,10 +135,52 @@ def test_fragment():
st.session_state.runs += 1
st.write("Runs:", st.session_state.runs)

# Slider 14
slider_11_value = st.slider(
"Slider 11 (formatted float)", value=0.05, step=0.2, format="%f%%"
)
st.write("Slider 11:", slider_11_value)

# Slider 15
slider_12_value = st.slider("Slider 12 (time-value)", value=time(12, 0))
st.write("Slider 12:", slider_12_value)

# Slider 16
st.slider(
"Label 13 - Overlapping on the left",
min_value=1e6 + 0,
max_value=1e6 + 100,
value=(1e6 + 0, 1e6 + 4),
)

# Slider 17
st.slider(
"Label 14 - Overlapping near the left",
min_value=1e6 + 0,
max_value=1e6 + 100,
value=(1e6 + 10, 1e6 + 14),
)

# Slider 18
st.slider(
"Label 15 - Overlapping on the right",
min_value=1e6 + 0,
max_value=1e6 + 100,
value=(1e6 + 96, 1e6 + 100),
)

# Slider 19
st.slider(
"Label 16 - Overlapping near the right",
min_value=1e6 + 0,
max_value=1e6 + 100,
value=(1e6 + 86, 1e6 + 90),
)

# Slider 20
st.slider(
"Label 17 - Overlapping near the center",
min_value=1e6 + 0,
max_value=1e6 + 100,
value=(1e6 + 48, 1e6 + 52),
)
7 changes: 6 additions & 1 deletion e2e_playwright/st_slider_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@

def test_slider_rendering(themed_app: Page, assert_snapshot: ImageCompareFunction):
st_sliders = themed_app.get_by_test_id("stSlider")
expect(st_sliders).to_have_count(16)
expect(st_sliders).to_have_count(21)

assert_snapshot(st_sliders.nth(4), name="st_slider-regular_with_format")
assert_snapshot(st_sliders.nth(7), name="st_slider-disabled")
assert_snapshot(st_sliders.nth(8), name="st_slider-hidden_label")
assert_snapshot(st_sliders.nth(9), name="st_slider-label_collapsed")
assert_snapshot(st_sliders.nth(10), name="st_slider-labels_overlap_slider")
assert_snapshot(st_sliders.nth(15), name="st_slider-time_value")
assert_snapshot(st_sliders.nth(16), name="st_slider-overlap_left")
assert_snapshot(st_sliders.nth(17), name="st_slider-overlap_near_left")
assert_snapshot(st_sliders.nth(18), name="st_slider-overlap_right")
assert_snapshot(st_sliders.nth(19), name="st_slider-overlap_near_right")
assert_snapshot(st_sliders.nth(20), name="st_slider-overlap_near_center")


def test_help_tooltip_works(app: Page):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,15 +380,15 @@ describe("#useDeckGl", () => {
expect(result.current.hasActiveSelection).toBe(false)
})

it("should be true when selection is not empty", async () => {
it("should be true when selection is not empty", () => {
const initialProps = getUseDeckGlProps({
selectionMode: [DeckGlJsonChartProto.SelectionMode.SINGLE_OBJECT],
})
const { result, rerender } = renderHook(props => useDeckGl(props), {
initialProps,
})

await act(async () => {
act(() => {
result.current.setSelection({
fromUi: true,
value: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { EmotionTheme } from "@streamlit/lib/src/theme"
import { DeckGlJsonChart as DeckGlJsonChartProto } from "@streamlit/lib/src/proto"
import {
useBasicWidgetClientState,
ValueWSource,
ValueWithSource,
} from "@streamlit/lib/src/useBasicWidgetState"
import { WidgetStateManager } from "@streamlit/lib/src/WidgetStateManager"
import { useRequiredContext } from "@streamlit/lib/src/hooks/useRequiredContext"
Expand Down Expand Up @@ -56,7 +56,7 @@ type UseDeckGlShape = {
onViewStateChange: (params: ViewStateChangeParameters) => void
selectionMode: DeckGlJsonChartProto.SelectionMode | undefined
setSelection: React.Dispatch<
React.SetStateAction<ValueWSource<DeckGlElementState> | null>
React.SetStateAction<ValueWithSource<DeckGlElementState> | null>
>
viewState: Record<string, unknown>
width: number | string
Expand Down Expand Up @@ -139,7 +139,7 @@ function getStateFromWidgetMgr(
function updateWidgetMgrState(
element: DeckGlJsonChartProto,
widgetMgr: WidgetStateManager,
vws: ValueWSource<DeckGlElementState>,
vws: ValueWithSource<DeckGlElementState>,
fragmentId?: string
): void {
if (!element.id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import { Placement } from "@streamlit/lib/src/components/shared/Tooltip"
import { labelVisibilityProtoValueToEnum } from "@streamlit/lib/src/util/utils"
import {
useBasicWidgetState,
ValueWSource,
ValueWithSource,
} from "@streamlit/lib/src/useBasicWidgetState"

export interface Props {
Expand Down Expand Up @@ -90,7 +90,7 @@ function getSingleSelection(currentSelection: number[]): number {
function syncWithWidgetManager(
element: ButtonGroupProto,
widgetMgr: WidgetStateManager,
valueWithSource: ValueWSource<ButtonGroupValue>,
valueWithSource: ValueWithSource<ButtonGroupValue>,
fragmentId?: string
): void {
widgetMgr.setIntArrayValue(
Expand Down Expand Up @@ -291,7 +291,7 @@ function ButtonGroup(props: Readonly<Props>): ReactElement {
} = element
const theme: EmotionTheme = useTheme()

const [value, setValueWSource] = useBasicWidgetState<
const [value, setValueWithSource] = useBasicWidgetState<
ButtonGroupValue,
ButtonGroupProto
>({
Expand All @@ -309,7 +309,7 @@ function ButtonGroup(props: Readonly<Props>): ReactElement {
index: number
): void => {
const newSelected = handleSelection(clickMode, index, value)
setValueWSource({ value: newSelected, fromUi: true })
setValueWithSource({ value: newSelected, fromUi: true })
}

let mode = undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe("Checkbox widget", () => {
)
})

it("resets its value when form is cleared", async () => {
it("resets its value when form is cleared", () => {
// Create a widget in a clearOnSubmit form
const props = getProps({ formId: "form" })
props.widgetMgr.setFormSubmitBehaviors("form", true)
Expand All @@ -178,7 +178,7 @@ describe("Checkbox widget", () => {
)

// "Submit" the form
await act(() => {
act(() => {
props.widgetMgr.submitForm("form", undefined)
})

Expand Down
32 changes: 17 additions & 15 deletions frontend/lib/src/components/widgets/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { Checkbox as CheckboxProto } from "@streamlit/lib/src/proto"
import { WidgetStateManager } from "@streamlit/lib/src/WidgetStateManager"
import {
useBasicWidgetState,
ValueWSource,
ValueWithSource,
} from "@streamlit/lib/src/useBasicWidgetState"
import { hasLightBackgroundColor } from "@streamlit/lib/src/theme"
import TooltipIcon from "@streamlit/lib/src/components/shared/TooltipIcon"
Expand All @@ -54,23 +54,25 @@ function Checkbox({
widgetMgr,
fragmentId,
}: Readonly<Props>): ReactElement {
const [value, setValueWSource] = useBasicWidgetState<boolean, CheckboxProto>(
{
getStateFromWidgetMgr,
getDefaultStateFromProto,
getCurrStateFromProto,
updateWidgetMgrState,
element,
widgetMgr,
fragmentId,
}
)
const [value, setValueWithSource] = useBasicWidgetState<
boolean,
CheckboxProto
>({
getStateFromWidgetMgr,
getDefaultStateFromProto,
getCurrStateFromProto,
updateWidgetMgrState,
element,
widgetMgr,
fragmentId,
})

const onChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>): void => {
setValueWSource({ value: e.target.checked, fromUi: true })
setValueWithSource({ value: e.target.checked, fromUi: true })
},
[setValueWSource]
// ESLint complains if we remove this unnecessary dep.
[setValueWithSource]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: see this comment about potentially leaving this dependency out

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to remove that dependency, but ESLint complains. I think it doesn't simply looks at the set* prefix, but actually checks whether the value came from useState/Callaback/Memo/etc.

frontend/lib/src/components/widgets/Checkbox/Checkbox.tsx
  75:5  warning  React Hook useCallback has a missing dependency: 'setValueWithSource'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh interesting! thanks for double-checking 👍 in this case we could also probably remove the comment again as that is the default behavior and I just did not know 😬

)

const theme = useTheme()
Expand Down Expand Up @@ -251,7 +253,7 @@ function getCurrStateFromProto(element: CheckboxProto): boolean {
function updateWidgetMgrState(
element: CheckboxProto,
widgetMgr: WidgetStateManager,
vws: ValueWSource<boolean>,
vws: ValueWithSource<boolean>,
fragmentId?: string
): void {
widgetMgr.setBoolValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ describe("ColorPicker widget", () => {
)
})

it("resets its value when form is cleared", async () => {
it("resets its value when form is cleared", () => {
// Create a widget in a clearOnSubmit form
const props = getProps({ formId: "form" })
jest.spyOn(props.widgetMgr, "setStringValue")
Expand Down Expand Up @@ -146,7 +146,7 @@ describe("ColorPicker widget", () => {
undefined
)

await act(async () => {
act(() => {
// "Submit" the form
props.widgetMgr.submitForm("form", undefined)
})
Expand Down
14 changes: 7 additions & 7 deletions frontend/lib/src/components/widgets/ColorPicker/ColorPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
* limitations under the License.
*/

import React, { FC, useCallback } from "react"
import React, { FC, memo, useCallback } from "react"

import { ColorPicker as ColorPickerProto } from "@streamlit/lib/src/proto"
import { WidgetStateManager } from "@streamlit/lib/src/WidgetStateManager"
import BaseColorPicker from "@streamlit/lib/src/components/shared/BaseColorPicker"
import { labelVisibilityProtoValueToEnum } from "@streamlit/lib/src/util/utils"
import {
useBasicWidgetState,
ValueWSource,
ValueWithSource,
} from "@streamlit/lib/src/useBasicWidgetState"

export interface Props {
Expand Down Expand Up @@ -61,7 +61,7 @@ const getCurrStateFromProto = (
const updateWidgetMgrState = (
element: ColorPickerProto,
widgetMgr: WidgetStateManager,
valueWithSource: ValueWSource<ColorPickerValue>,
valueWithSource: ValueWithSource<ColorPickerValue>,
fragmentId?: string
): void => {
widgetMgr.setStringValue(
Expand All @@ -79,7 +79,7 @@ const ColorPicker: FC<Props> = ({
width,
fragmentId,
}) => {
const [value, setValueWSource] = useBasicWidgetState<
const [value, setValueWithSource] = useBasicWidgetState<
ColorPickerValue,
ColorPickerProto
>({
Expand All @@ -94,9 +94,9 @@ const ColorPicker: FC<Props> = ({

const handleColorClose = useCallback(
(color: string): void => {
setValueWSource({ value: color, fromUi: true })
setValueWithSource({ value: color, fromUi: true })
},
[setValueWSource]
[setValueWithSource]
)

return (
Expand All @@ -114,4 +114,4 @@ const ColorPicker: FC<Props> = ({
)
}

export default ColorPicker
export default memo(ColorPicker)
Loading