/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
frontend/lib/src/components/core/Layout/useLayoutStyles.test.tsx
1 153 строки
37 KB
Lukas Masuch
Split `Arrow.proto` into `Table.proto` and `Dataframe.proto` (#13768)
04 фев 2026, 22:22
Не верифицирован
04 фев 2026, 22:22
dc9d8fe
Код
Авторство
О чём код?
import { ReactNode } from "react" /** * Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { renderHook } from "@testing-library/react" import { describe, expect, it } from "vitest" import { Element, IAlert, streamlit } from "@streamlit/protobuf" import { FlexContextProvider } from "./FlexContext" import { useLayoutStyles, UseLayoutStylesShape } from "./useLayoutStyles" import { Direction, MinFlexElementWidth } from "./utils" function withFlexContextProvider( direction: Direction, isInContentWidthContainer: boolean = false ) { return function Wrapper({ children }: { children: ReactNode }) { return ( <FlexContextProvider direction={direction} hasContentWidth={isInContentWidthContainer} > {children} </FlexContextProvider> ) } } class MockElement implements Element { widthConfig?: streamlit.WidthConfig | null heightConfig?: streamlit.HeightConfig | null type?: "imgs" | "textArea" | "iframe" | "deckGlJsonChart" | "dataframe" constructor(props: Partial<MockElement> = {}) { Object.assign(this, props) } toJSON(): MockElement { return this } } const getDefaultStyles = ( overrides: Partial<UseLayoutStylesShape> ): UseLayoutStylesShape => { const defaults = { width: "auto" as const, height: "auto" as const, overflow: "visible" as const, } return { ...defaults, ...overrides } } describe("#useLayoutStyles", () => { describe("with an element", () => { describe("that has useContainerWidth set to a falsy value", () => { const useContainerWidth = false it.each([ [undefined, getDefaultStyles({})], [0, getDefaultStyles({})], [-100, getDefaultStyles({})], [NaN, getDefaultStyles({})], [100, getDefaultStyles({ width: "100px" })], ])("and with a width value of %s, returns %o", (width, expected) => { const element = new MockElement() const subElement = { width, useContainerWidth } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) }) }) describe('that has useContainerWidth set to "true"', () => { const useContainerWidth = true it.each([ [undefined, getDefaultStyles({ width: "100%" })], [0, getDefaultStyles({ width: "100%" })], [-100, getDefaultStyles({ width: "100%" })], [NaN, getDefaultStyles({ width: "100%" })], [100, getDefaultStyles({ width: "100%" })], ])("and with a width value of %s, returns %o", (width, expected) => { const element = new MockElement() const subElement = { width, useContainerWidth } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) }) }) describe("that has widthConfig set", () => { it.each([ [ new streamlit.WidthConfig({ useStretch: true }), false, getDefaultStyles({ width: "100%" }), ], [ new streamlit.WidthConfig({ useStretch: true }), true, getDefaultStyles({ width: "100%" }), ], [ new streamlit.WidthConfig({ useContent: true }), false, getDefaultStyles({ width: "fit-content" }), ], [ new streamlit.WidthConfig({ useContent: true }), true, getDefaultStyles({ width: "100%" }), ], [ new streamlit.WidthConfig({ pixelWidth: 100 }), false, getDefaultStyles({ width: "100px" }), ], [ new streamlit.WidthConfig({ pixelWidth: 100 }), true, getDefaultStyles({ width: "100%" }), ], ])( "and with a widthConfig value of %o and useContainerWidth %s, returns %o", (widthConfig, useContainerWidth, expected) => { const element = new MockElement({ widthConfig }) const subElement = { useContainerWidth } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) } ) }) describe("that has widthConfig set to zero pixelWidth values", () => { it.each([ [0, false, getDefaultStyles({ width: "0px" })], [0, true, getDefaultStyles({ width: "100%" })], ])( "and with a pixelWidth value of %s and useContainerWidth %s, returns %o", (pixelWidth, useContainerWidth, expected) => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth }), }) const subElement = { useContainerWidth } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) } ) }) describe("that has widthConfig set to invalid pixelWidth values", () => { it.each([ [-100, false, getDefaultStyles({})], [NaN, false, getDefaultStyles({})], [100, false, getDefaultStyles({ width: "100px" })], [-100, true, getDefaultStyles({ width: "100%" })], [NaN, true, getDefaultStyles({ width: "100%" })], [100, true, getDefaultStyles({ width: "100%" })], ])( "and with a pixelWidth value of %s and useContainerWidth %s, returns %o", (pixelWidth, useContainerWidth, expected) => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth }), }) const subElement = { useContainerWidth } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) } ) }) describe("with variations on element", () => { it.each([ [ { widthConfig: undefined, useContainerWidth: false }, getDefaultStyles({}), ], [ { widthConfig: undefined, useContainerWidth: true }, getDefaultStyles({ width: "100%" }), ], ])("and with element %o, returns %o", (props, expected) => { const element = new MockElement({ widthConfig: props.widthConfig, }) const subElement = { useContainerWidth: props.useContainerWidth } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) }) }) describe("with width included along with widthConfig", () => { it.each([ [ { widthConfig: new streamlit.WidthConfig({ useStretch: true }), width: 100, }, false, getDefaultStyles({ width: "100%" }), ], [ { widthConfig: new streamlit.WidthConfig({ useContent: true }), width: 100, }, false, getDefaultStyles({ width: "fit-content" }), ], [ { widthConfig: new streamlit.WidthConfig({ pixelWidth: 200 }), width: 100, }, false, getDefaultStyles({ width: "200px" }), ], [ { widthConfig: new streamlit.WidthConfig({ pixelWidth: 200 }), width: 100, }, true, getDefaultStyles({ width: "100%" }), ], [ { widthConfig: new streamlit.WidthConfig({ pixelWidth: 0 }), width: 100, }, false, getDefaultStyles({ width: "0px" }), ], [ { widthConfig: new streamlit.WidthConfig({ pixelWidth: -100 }), width: 100, }, false, getDefaultStyles({}), ], [ { widthConfig: new streamlit.WidthConfig({ pixelWidth: NaN }), width: 100, }, false, getDefaultStyles({}), ], ])( "and with element props %o and useContainerWidth %s, returns %o", (props, useContainerWidth, expected) => { const element = new MockElement({ widthConfig: props.widthConfig, }) const subElement = { width: props.width, useContainerWidth, } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) } ) }) describe("with width defined on subElement but element.widthConfig is null or undefined", () => { describe("with valid width values", () => { it.each([ [100, false, null, getDefaultStyles({ width: "100px" })], [200, false, null, getDefaultStyles({ width: "200px" })], [100, true, null, getDefaultStyles({ width: "100%" })], [100, false, undefined, getDefaultStyles({ width: "100px" })], [200, false, undefined, getDefaultStyles({ width: "200px" })], [100, true, undefined, getDefaultStyles({ width: "100%" })], ])( "and with a width value of %s, useContainerWidth %s, and widthConfig %s, returns %o", (width, useContainerWidth, widthConfig, expected) => { const element = new MockElement({ widthConfig, }) const subElement = { width, useContainerWidth, } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) } ) }) describe("with invalid width values", () => { it.each([ [0, false, null, getDefaultStyles({})], [0, false, undefined, getDefaultStyles({})], [-100, false, null, getDefaultStyles({})], [-100, false, undefined, getDefaultStyles({})], ])( "and with a width value of %s, useContainerWidth %s, and widthConfig %s, returns %o", (width, useContainerWidth, widthConfig, expected) => { const element = new MockElement({ widthConfig, }) const subElement = { width, useContainerWidth, } const { result } = renderHook(() => useLayoutStyles({ element, subElement }) ) expect(result.current).toEqual(expected) } ) }) }) describe("that has heightConfig set", () => { it.each([ [ new streamlit.HeightConfig({ useStretch: true }), getDefaultStyles({ height: "100%" }), ], [ new streamlit.HeightConfig({ useContent: true }), getDefaultStyles({}), ], [ new streamlit.HeightConfig({ pixelHeight: 100 }), getDefaultStyles({ height: "100px", overflow: "auto", flex: "0 0 100px", }), ], ])( "and with a heightConfig value of %o, returns %o", (heightConfig, expected) => { const element = new MockElement({ heightConfig }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current).toEqual(expected) } ) }) describe("that has heightConfig set to zero pixelHeight values", () => { it.each([[0, getDefaultStyles({ height: "0px", overflow: "auto" })]])( "and with a pixelHeight value of %s, returns %o", (pixelHeight, expected) => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ pixelHeight }), }) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual(expected) } ) }) describe("that has heightConfig set to invalid pixelHeight values", () => { it.each([ [-100, getDefaultStyles({})], [NaN, getDefaultStyles({})], ])( "and with a pixelHeight value of %s, returns %o", (pixelHeight, expected) => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ pixelHeight }), }) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual(expected) } ) }) describe("with height defined on subElement but element.heightConfig is null or undefined", () => { it.each([ [ 100, null, getDefaultStyles({ height: "100px", overflow: "auto", flex: "0 0 100px", }), ], [ 200, null, getDefaultStyles({ height: "200px", overflow: "auto", flex: "0 0 200px", }), ], [0, null, getDefaultStyles({})], [-100, null, getDefaultStyles({})], [NaN, null, getDefaultStyles({})], [ 100, undefined, getDefaultStyles({ height: "100px", overflow: "auto", flex: "0 0 100px", }), ], [ 200, undefined, getDefaultStyles({ height: "200px", overflow: "auto", flex: "0 0 200px", }), ], [0, undefined, getDefaultStyles({})], [-100, undefined, getDefaultStyles({})], [NaN, undefined, getDefaultStyles({})], ])( "and with a height value of %s and heightConfig %s, returns %o", (height, heightConfig, expected) => { const element = new MockElement({ heightConfig }) const subElement = { height } const { result } = renderHook( () => useLayoutStyles({ element, subElement }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual(expected) } ) }) describe("with height included in subElement", () => { describe("with valid height values", () => { it.each([ [undefined, getDefaultStyles({})], [ 100, getDefaultStyles({ height: "100px", overflow: "auto", flex: "0 0 100px", }), ], ])("and with a height value of %s, returns %o", (height, expected) => { const element = new MockElement() const subElement = { height } const { result } = renderHook( () => useLayoutStyles({ element, subElement }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual(expected) }) }) describe("with invalid height values", () => { it.each([ [0, getDefaultStyles({})], [-100, getDefaultStyles({})], [NaN, getDefaultStyles({})], ])("and with a height value of %s, returns %o", (height, expected) => { const element = new MockElement() const subElement = { height } const { result } = renderHook( () => useLayoutStyles({ element, subElement }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual(expected) }) }) }) describe("with height included along with heightConfig", () => { describe("with valid heightConfig values", () => { it.each([ [ { heightConfig: new streamlit.HeightConfig({ useStretch: true }), height: 100, }, getDefaultStyles({ height: "100%" }), ], [ { heightConfig: new streamlit.HeightConfig({ useContent: true }), height: 100, }, getDefaultStyles({}), ], [ { heightConfig: new streamlit.HeightConfig({ pixelHeight: 200 }), height: 100, }, getDefaultStyles({ height: "200px", overflow: "auto", flex: "0 0 200px", }), ], [ { heightConfig: new streamlit.HeightConfig({ pixelHeight: 0 }), height: 100, }, getDefaultStyles({ height: "0px", overflow: "auto", flex: "0 0 0px", }), ], ])("and with element props %o, returns %o", (props, expected) => { const element = new MockElement({ heightConfig: props.heightConfig }) const subElement = { height: props.height } const { result } = renderHook( () => useLayoutStyles({ element, subElement }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual(expected) }) }) describe("with invalid heightConfig values", () => { it.each([ [ { heightConfig: new streamlit.HeightConfig({ pixelHeight: -100 }), height: 100, }, getDefaultStyles({}), ], [ { heightConfig: new streamlit.HeightConfig({ pixelHeight: NaN }), height: 100, }, getDefaultStyles({}), ], ])("and with element props %o, returns %o", (props, expected) => { const element = new MockElement({ heightConfig: props.heightConfig }) const subElement = { height: props.height } const { result } = renderHook( () => useLayoutStyles({ element, subElement }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual(expected) }) }) }) describe("with both width and height configurations", () => { it.each([ [ { widthConfig: new streamlit.WidthConfig({ pixelWidth: 200 }), heightConfig: new streamlit.HeightConfig({ pixelHeight: 300 }), }, getDefaultStyles({ width: "200px", height: "300px", overflow: "auto", flex: "0 0 300px", }), ], [ { widthConfig: new streamlit.WidthConfig({ useStretch: true }), heightConfig: new streamlit.HeightConfig({ useStretch: true }), }, getDefaultStyles({ width: "100%", height: "100%" }), ], [ { widthConfig: new streamlit.WidthConfig({ useContent: true }), heightConfig: new streamlit.HeightConfig({ useContent: true }), }, getDefaultStyles({ width: "fit-content", height: "auto" }), ], ])("and with element props %o, returns %o", (props, expected) => { const element = new MockElement(props) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current).toEqual(expected) }) }) describe("with both width and height in subElement", () => { it.each([ [ { width: 200, height: 300 }, getDefaultStyles({ width: "200px", height: "300px", overflow: "auto", flex: "0 0 300px", }), ], [ { width: 0, height: 100 }, getDefaultStyles({ height: "100px", overflow: "auto", flex: "0 0 100px", }), ], [ { width: 100, height: 0 }, getDefaultStyles({ width: "100px", }), ], ])( "and with subElement props %o, returns %o", (subElementProps, expected) => { const element = new MockElement() const { result } = renderHook( () => useLayoutStyles({ element, subElement: subElementProps }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual(expected) } ) }) describe("with widthConfig on the subElement (using type assertions)", () => { it.each([ [ { subElementWidthConfig: new streamlit.WidthConfig({ useStretch: true, }), }, getDefaultStyles({ width: "100%", height: "auto" }), ], [ { subElementWidthConfig: new streamlit.WidthConfig({ useContent: true, }), }, getDefaultStyles({ width: "fit-content", height: "auto" }), ], [ { subElementWidthConfig: new streamlit.WidthConfig({ pixelWidth: 150, }), }, getDefaultStyles({ width: "150px", height: "auto" }), ], ])( "and with subElement widthConfig %o, returns %o", (props, expected) => { const element = new MockElement() // Use type assertion to bypass TypeScript checks const subElement = { widthConfig: props.subElementWidthConfig, } as IAlert const { result } = renderHook(() => useLayoutStyles({ element, subElement, }) ) expect(result.current).toEqual(expected) } ) }) describe("flex property behavior with direction context", () => { it("should include flex for vertical direction with pixel height", () => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ pixelHeight: 250 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current.flex).toBe("0 0 250px") }) it("should include flex for horizontal direction with pixel width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 120 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), }) expect(result.current.flex).toBe("0 0 120px") }) it("should not include flex for vertical direction with pixel width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 120 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current.flex).toBeUndefined() }) it("should not include flex for vertical direction with stretch height", () => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ useStretch: true }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current.flex).toBeUndefined() }) it("should not include flex for horizontal direction with pixel height", () => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ pixelHeight: 250 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), }) expect(result.current.flex).toBeUndefined() }) it("should include flex for horizontal direction with stretch width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ useStretch: true }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), }) expect(result.current.flex).toBe("1 1 fit-content") }) it("should not include flex for vertical direction with content height", () => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ useContent: true }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current.flex).toBeUndefined() }) it("should include flex for horizontal direction with content width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ useContent: true }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), }) expect(result.current.flex).toBe("0 0 fit-content") }) }) describe("minStretchBehavior behavior", () => { it("should use provided minStretchBehavior value", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ useStretch: true }), }) const minStretchBehavior: MinFlexElementWidth = "14rem" const { result } = renderHook( () => useLayoutStyles({ element, minStretchBehavior }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), } ) expect(result.current.flex).toBe("1 1 14rem") }) it("should use default fit-content when minStretchBehavior is undefined", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ useStretch: true }), }) const minStretchBehavior: MinFlexElementWidth = undefined const { result } = renderHook( () => useLayoutStyles({ element, minStretchBehavior }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), } ) expect(result.current.flex).toBe("1 1 fit-content") }) it("should not affect non-stretch width configurations", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ useContent: true }), }) const minStretchBehavior: MinFlexElementWidth = "14rem" const { result } = renderHook( () => useLayoutStyles({ element, minStretchBehavior }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), } ) expect(result.current.flex).toBe("0 0 fit-content") }) it("should not affect vertical direction even with stretch width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ useStretch: true }), }) const minStretchBehavior: MinFlexElementWidth = "14rem" const { result } = renderHook( () => useLayoutStyles({ element, minStretchBehavior }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current.flex).toBeUndefined() }) it("should not affect pixel width configurations", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 200 }), }) const minStretchBehavior: MinFlexElementWidth = "14rem" const { result } = renderHook( () => useLayoutStyles({ element, minStretchBehavior }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), } ) expect(result.current.flex).toBe("0 0 200px") }) }) describe("that has remWidth in widthConfig", () => { it.each([ [ new streamlit.WidthConfig({ remWidth: 2.5 }), getDefaultStyles({ width: "2.5rem" }), ], [ new streamlit.WidthConfig({ remWidth: 0.75 }), getDefaultStyles({ width: "0.75rem" }), ], [ new streamlit.WidthConfig({ remWidth: 4.25 }), getDefaultStyles({ width: "4.25rem" }), ], ])( "and with a widthConfig value of %o, returns %o", (widthConfig, expected) => { const element = new MockElement({ widthConfig }) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual(expected) } ) }) describe("that has remHeight in heightConfig", () => { it.each([ [ new streamlit.HeightConfig({ remHeight: 2.5 }), getDefaultStyles({ height: "2.5rem" }), ], [ new streamlit.HeightConfig({ remHeight: 0.75 }), getDefaultStyles({ height: "0.75rem" }), ], [ new streamlit.HeightConfig({ remHeight: 4.25 }), getDefaultStyles({ height: "4.25rem" }), ], ])( "and with a heightConfig value of %o, returns %o", (heightConfig, expected) => { const element = new MockElement({ heightConfig }) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual(expected) } ) }) describe("flex property with rem dimensions", () => { it("should include flex for horizontal direction with rem width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ remWidth: 2.5 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), }) expect(result.current.flex).toBe("0 0 2.5rem") expect(result.current.width).toBe("2.5rem") }) it("should include flex for vertical direction with rem height", () => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ remHeight: 4.25 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current.flex).toBe("0 0 4.25rem") expect(result.current.height).toBe("4.25rem") }) it("should not include flex for vertical direction with rem width", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ remWidth: 2.5 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current.flex).toBeUndefined() expect(result.current.width).toBe("2.5rem") }) it("should not include flex for horizontal direction with rem height", () => { const element = new MockElement({ heightConfig: new streamlit.HeightConfig({ remHeight: 4.25 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.HORIZONTAL), }) expect(result.current.flex).toBeUndefined() expect(result.current.height).toBe("4.25rem") }) }) describe("with both rem width and rem height", () => { it("applies both rem dimensions correctly", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ remWidth: 2.5 }), heightConfig: new streamlit.HeightConfig({ remHeight: 4.25 }), }) const { result } = renderHook(() => useLayoutStyles({ element }), { wrapper: withFlexContextProvider(Direction.VERTICAL), }) expect(result.current).toEqual( getDefaultStyles({ width: "2.5rem", height: "4.25rem", flex: "0 0 4.25rem", }) ) }) }) describe("with styleOverrides", () => { it("applies style overrides to computed styles", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 200 }), heightConfig: new streamlit.HeightConfig({ pixelHeight: 300 }), }) const styleOverrides = { width: "50%", height: "150px", overflow: "hidden", flex: "0 0 150px", } const { result } = renderHook(() => useLayoutStyles({ element, styleOverrides }) ) expect(result.current).toEqual({ width: "50%", height: "150px", overflow: "hidden", flex: "0 0 150px", }) }) it("partially overrides computed styles", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 200 }), heightConfig: new streamlit.HeightConfig({ pixelHeight: 300 }), }) const styleOverrides = { width: "75%", } const { result } = renderHook( () => useLayoutStyles({ element, styleOverrides }), { wrapper: withFlexContextProvider(Direction.VERTICAL), } ) expect(result.current).toEqual({ width: "75%", height: "300px", overflow: "auto", flex: "0 0 300px", }) }) it("handles empty styleOverrides", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 100 }), }) const styleOverrides = {} const { result } = renderHook(() => useLayoutStyles({ element, styleOverrides }) ) expect(result.current).toEqual({ width: "100px", height: "auto", overflow: "visible", }) }) it("handles undefined styleOverrides", () => { const element = new MockElement({ widthConfig: new streamlit.WidthConfig({ pixelWidth: 100 }), }) const { result } = renderHook(() => useLayoutStyles({ element, styleOverrides: undefined }) ) expect(result.current).toEqual({ width: "100px", height: "auto", overflow: "visible", }) }) }) describe("with text alignment configuration", () => { it.each([ [streamlit.TextAlignmentConfig.Alignment.LEFT, "left"], [streamlit.TextAlignmentConfig.Alignment.CENTER, "center"], [streamlit.TextAlignmentConfig.Alignment.RIGHT, "right"], [streamlit.TextAlignmentConfig.Alignment.JUSTIFY, "justify"], ])( "applies text-align: %s for alignment %s", (alignment, expectedTextAlign) => { const element = Element.create({ textAlignmentConfig: { alignment }, }) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual({ width: "auto", height: "auto", overflow: "visible", textAlign: expectedTextAlign, }) } ) it("does not apply text-align when textAlignmentConfig is not set", () => { const element = Element.create({}) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual({ width: "auto", height: "auto", overflow: "visible", textAlign: undefined, }) }) it("applies text-align with other layout properties", () => { const element = Element.create({ widthConfig: { useStretch: true }, heightConfig: { pixelHeight: 200 }, textAlignmentConfig: { alignment: streamlit.TextAlignmentConfig.Alignment.CENTER, }, }) const { result } = renderHook(() => useLayoutStyles({ element })) expect(result.current).toEqual({ width: "100%", height: "200px", overflow: "auto", textAlign: "center", }) }) it("allows styleOverrides to override text-align", () => { const element = Element.create({ textAlignmentConfig: { alignment: streamlit.TextAlignmentConfig.Alignment.CENTER, }, }) const styleOverrides = { textAlign: "right" as const } const { result } = renderHook(() => useLayoutStyles({ element, styleOverrides }) ) expect(result.current).toEqual({ width: "auto", height: "auto", overflow: "visible", textAlign: "right", // Override value }) }) }) }) })