Skip to content

Commit

Permalink
Feature: Colored dividers for st.header & st.subheader (#7133)
Browse files Browse the repository at this point in the history
Added a divider param to st.header & st.subheader, takes a str (supported colors: “blue”, “green”, “orange”, “red”, “violet”, “gray”/"grey", or “rainbow”) or bool. If divider=True, the divider color will cycle through selected options (blue -> green -> orange -> red -> violet -> blue ...)
  • Loading branch information
mayagbarnes authored Aug 18, 2023
1 parent 7902bfd commit 9c91b7d
Show file tree
Hide file tree
Showing 74 changed files with 503 additions and 98 deletions.
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.
66 changes: 66 additions & 0 deletions e2e/playwright/st_headings_divider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022)
#
# 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 streamlit as st

# Headers with specified colors
st.header("Blue Divider:", divider="blue")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Green Divider:", divider="green")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Orange Divider:", divider="orange")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Red Divider:", divider="red")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Violet Divider:", divider="violet")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Gray Divider:", divider="gray")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Rainbow Divider:", divider="rainbow")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Grey Divider:", divider="grey")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")


# Subheaders with specified colors
st.subheader("Blue Divider:", divider="blue")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Green Divider:", divider="green")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Orange Divider:", divider="orange")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Red Divider:", divider="red")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Violet Divider:", divider="violet")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Gray Divider:", divider="gray")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Rainbow Divider:", divider="rainbow")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Grey Divider:", divider="grey")
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
38 changes: 38 additions & 0 deletions e2e/playwright/st_headings_divider_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022)
#
# 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.


from playwright.sync_api import Page, expect

from conftest import ImageCompareFunction


def test_header_display(app: Page, assert_snapshot: ImageCompareFunction):
"""Test that st.header renders correctly with dividers."""
header_elements = app.locator(".stHeadingContainer")
expect(header_elements).to_have_count(16)

for i, element in enumerate(header_elements.all()):
if i < 8:
assert_snapshot(element, name=f"header-divider-{i}")


def test_subheader_display(app: Page, assert_snapshot: ImageCompareFunction):
"""Test that st.subheader renders correctly with dividers."""
subheader_elements = app.locator(".stHeadingContainer")
expect(subheader_elements).to_have_count(16)

for i, element in enumerate(subheader_elements.all()):
if i > 7:
assert_snapshot(element, name=f"subheader-divider-{i}")
34 changes: 34 additions & 0 deletions e2e/scripts/st_heading_divider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022)
#
# 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 streamlit as st

# Headers/Subheaders should cycle through selected colors when set to True
st.header("Blue Divider:", divider=True)
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Green Divider:", divider=True)
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Orange Divider:", divider=True)
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Red Divider:", divider=True)
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.header("Violet Divider:", divider=True)
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")

st.subheader("Blue Divider:", divider=True)
st.write("Lorem ipsum dolor sit amet, consectetur adipiscing elit")
32 changes: 32 additions & 0 deletions e2e/specs/st_heading_divider.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022)
*
* 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.
*/

describe("st.header & st.subheader dividers", () => {
before(() => {
cy.loadApp("http://localhost:3000/");
});

it("displays correctly when divider=True", () => {
cy.get(".element-container .stMarkdown h2").should("have.length", 3);
cy.get(".element-container .stMarkdown h3").should("have.length", 3);
cy.get(".stHeadingContainer").should("have.length", 6)

const expectedColors = [ "blue", "green", "orange", "red", "violet", "blue"]
expectedColors.forEach( (color, idx) => {
cy.getIndexed(".stHeadingContainer", idx).matchThemedSnapshots(`divider-${color}${idx}`)
})
});
});
4 changes: 4 additions & 0 deletions frontend/lib/src/components/core/Block/Block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import React, { ReactElement } from "react"
import { useTheme } from "@emotion/react"
import { AutoSizer } from "react-virtualized"

import { Block as BlockProto } from "@streamlit/lib/src/proto"
Expand All @@ -29,6 +30,7 @@ import {
BaseBlockProps,
isComponentStale,
shouldComponentBeEnabled,
assignDividerColor,
} from "./utils"
import ElementNodeRenderer from "./ElementNodeRenderer"

Expand Down Expand Up @@ -144,6 +146,8 @@ const BlockNodeRenderer = (props: BlockPropsWithWidth): ReactElement => {
}

const ChildRenderer = (props: BlockPropsWithWidth): ReactElement => {
// Handle cycling of colors for dividers:
assignDividerColor(props.node, useTheme())
return (
<>
{props.node.children &&
Expand Down
33 changes: 32 additions & 1 deletion frontend/lib/src/components/core/Block/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { ScriptRunState } from "@streamlit/lib/src/ScriptRunState"
import { AppNode } from "@streamlit/lib/src/AppNode"
import { BlockNode, AppNode } from "@streamlit/lib/src/AppNode"
import {
FormsData,
WidgetStateManager,
Expand All @@ -24,6 +24,7 @@ import { FileUploadClient } from "@streamlit/lib/src/FileUploadClient"
import { ComponentRegistry } from "@streamlit/lib/src/components/widgets/CustomComponent"
import { SessionInfo } from "@streamlit/lib/src/SessionInfo"
import { StreamlitEndpoints } from "@streamlit/lib/src/StreamlitEndpoints"
import { EmotionTheme, getDividerColors } from "@streamlit/lib/src/theme"

export function shouldComponentBeEnabled(
elementType: string,
Expand Down Expand Up @@ -57,6 +58,36 @@ export function isComponentStale(
return !enable || isElementStale(node, scriptRunState, scriptRunId)
}

export function assignDividerColor(
node: BlockNode,
theme: EmotionTheme
): void {
// All available divider colors
const allColorMap = getDividerColors(theme)
const allColorKeys = Object.keys(allColorMap)

// Limited colors for auto assignment
const { blue, green, orange, red, violet } = allColorMap
const autoColorMap = { blue, green, orange, red, violet }
const autoColorKeys = Object.keys(autoColorMap)
let dividerIndex = 0

Array.from(node.getElements()).forEach(element => {
const divider = element.heading?.divider
if (element.type === "heading" && divider) {
if (divider === "auto") {
const colorKey = autoColorKeys[dividerIndex]
// @ts-expect-error - heading.divider is not undefined at this point
element.heading.divider = autoColorMap[colorKey]
dividerIndex += 1
if (dividerIndex === autoColorKeys.length) dividerIndex = 0
} else if (allColorKeys.includes(divider)) {
// @ts-expect-error
element.heading.divider = allColorMap[divider]
}
}
})
}
export interface BaseBlockProps {
/**
* The app's StreamlitEndpoints instance. Exposes non-websocket endpoint logic
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Spinner component renders without crashing 1`] = `"<div class=\\"css-yprxax-StyledSpinnerContainer e1se5lgy0\\"><i class=\\"css-55asu9-ThemedStyledSpinner e1se5lgy1 \\"></i><div data-testid=\\"stMarkdownContainer\\" class=\\"css-1cwy3ln-StyledStreamlitMarkdown e1nzilvr4\\"><p>Loading...</p></div></div>"`;
exports[`Spinner component renders without crashing 1`] = `"<div class=\\"css-yprxax-StyledSpinnerContainer e1se5lgy0\\"><i class=\\"css-55asu9-ThemedStyledSpinner e1se5lgy1 \\"></i><div data-testid=\\"stMarkdownContainer\\" class=\\"css-1cwy3ln-StyledStreamlitMarkdown e1nzilvr5\\"><p>Loading...</p></div></div>"`;
Loading

0 comments on commit 9c91b7d

Please sign in to comment.