Skip to content

Conversation

@sfc-gh-tteixeira
Copy link
Contributor

@sfc-gh-tteixeira sfc-gh-tteixeira commented Mar 29, 2023

📚 Context

When iframing Streamlit, this PR makes it possible for the host frame to update Streamlit's theme in some detail.

Note that this is not meant to be a public API (yet??)

What this PR does

  • Adds frontend/hostframe.html to test host-guest communication. See that file for usage.
    • Allows localhost postmessage when in dev mode, so hostframe.html can do its thing.
  • Adds ability to theme certain properties using postMessage from Host to Guest. The themable
    properties are:
    • All normal theme colors from config.toml
    • Widget background color. This was separated from secondaryBackgroundColor, which is now only
      used in the sidebar.
    • Widget border radius. NOTE: This is not working quite right! Need to figure out why before merging.
      • To support this, we changed checkboxes so they use radii.sm. This fixes the checkbox when using
        Snowsight's theme, and is a barely noticeable change when using Streamlit's.
      • Now only md and sm radii are used in streamlit, and sm only used in st.checkbox. So these
        are called basewidgetradius and checkboxradius in the postmessage.
    • Font sizes. You can adjust md, sm, and twosm font sizes, but not anything else. This is because (1) the first two are ubiquitous, (2) the last one is so small that it needs to be adjusted on a per-pixel basis, and (3) everything else is derived from md, through the use of rem units, so they auto-adjust. We can always support specifically setting those in the future, though.
    • Font families. You can pass @font-face declarations to Streamlit via postMessage now.

What's missing:

  • Tests for the new functionality
  • Fixing the widget radius bug mentioned above.

  • What kind of change does this PR introduce?

    • Bugfix
    • Feature
    • Refactoring
    • Other, please describe:

🧠 Description of Changes

  • Add bullet points summarizing your changes here

    • This is a breaking API change
    • This is a visible (user-facing) change

Revised:

Insert screenshot of your updated UI/code here

Current:

Insert screenshot of existing UI/code here

🧪 Testing Done

  • Screenshots included
  • Added/Updated unit tests
  • Added/Updated e2e tests

🌐 References

Does this depend on other work, documents, or tickets?

  • Issue: Closes #XXXX

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@mayagbarnes
Copy link
Collaborator

Just in case its helpful, worked on a couple potential FE tests -> For ThemedApp.test.tsx

  import { act } from "react-dom/test-utils"
  import FontFaceDeclaration from "./components/core/FontFaceDeclaration"

  const mockCustomThemeConfig = {
    primaryColor: "#1A6CE7",
    backgroundColor: "#FFFFFF",
    secondaryBackgroundColor: "#F5F5F5",
    textColor: "#1A1D21",
    widgetBackgroundColor: "#FFFFFF",
    widgetBorderColor: "#D3DAE8",
    fontFaces: [
      {
        family: "Inter",
        url: "https://rsms.me/inter/font-files/Inter-Regular.woff2?v=3.19",
        weight: 400,
      },
    ],
  }


  it("handles custom theme sent from Host", () => {
    const wrapper = mount(<ThemedApp />)
    
    let fontFaceComponent = wrapper.find(FontFaceDeclaration)
    expect(fontFaceComponent.exists()).toBe(false)

    const props = wrapper.find(AppWithScreencast).props()
    act(() => {
      props.theme.setImportedTheme(mockCustomThemeConfig)
    })
    
    wrapper.update()

    const updatedTheme: ThemeConfig = wrapper.find(AppWithScreencast).props().theme.activeTheme
    expect(updatedTheme.name).toBe(CUSTOM_THEME_NAME)
    expect(updatedTheme.emotion.genericColors.primary).toBe(mockCustomThemeConfig.primaryColor)

    fontFaceComponent = wrapper.find(FontFaceDeclaration)
    expect(fontFaceComponent.exists()).toBe(true)
    expect(fontFaceComponent.props().fontFaces).toEqual(mockCustomThemeConfig.fontFaces)
  })

For withHostCommunication.test.tsx

const mockCustomThemeConfig = {
  primaryColor: "#1A6CE7",
  backgroundColor: "#FFFFFF",
  secondaryBackgroundColor: "#F5F5F5",
  textColor: "#1A1D21",
  widgetBackgroundColor: "#FFFFFF",
  widgetBorderColor: "#D3DAE8",
}

  it("can process a received SET_CUSTOM_THEME_CONFIG message", () => {
    act(() => {
      dispatchEvent(
        "message",
        new MessageEvent("message", {
          data: {
            stCommVersion: HOST_COMM_VERSION,
            type: "SET_CUSTOM_THEME_CONFIG",
            theme: mockCustomThemeConfig,
          },
          origin: "http://devel.streamlit.test",
        })
      )
    })

    wrapper.update()

    const theme = wrapper.find(TestComponent).prop("theme")
    expect(theme.setImportedTheme).toHaveBeenCalledWith(mockCustomThemeConfig)
  })

@mayagbarnes mayagbarnes added the security-assessment-completed Security assessment has been completed for PR label Mar 30, 2023
@mayagbarnes
Copy link
Collaborator

Looks like only remaining test failure is in App.tsx for the "does nothing if the custom theme received from server has a matching hash" test -> the hash comparison is failing, so addThemes and setTheme are called.
This is a result of the customThemeConfig constructor initializing a default empty array for the fontFaces key, while the NewSession constructor doesn't include an empty fontFaces key at all. This is a protobuf issue and Ken/I don't believe it will be problematic, so just updating the test should be okay.
In NEW_SESSION_JSON object, resolve per below:

  const NEW_SESSION_JSON = {
    config: {
      gatherUsageStats: false,
      maxCachedMessageAge: 0,
      mapboxToken: "mapboxToken",
      allowRunOnSave: false,
      hideSidebarNav: false,
    },
    customTheme: {
      primaryColor: "red",
      fontFaces: [],
    },
// .... rest of the stuff here

@sfc-gh-jgarcia
Copy link
Contributor

c3e6026

Thanks a lot for looking into this, @mayagbarnes! This test had been fixed on c3e6026.

Let me know if you need anything else here!

Copy link
Collaborator

@mayagbarnes mayagbarnes left a comment

Choose a reason for hiding this comment

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

Please see comment to fix withHostCommunication/types.ts discrepancy, as well as clarification below on intended behavior, but otherwise LGTM 👍🏼

Will ask Vincent for protobuf codeowner review at standup tomorrow

@mayagbarnes
Copy link
Collaborator

mayagbarnes commented Mar 31, 2023

Also when I test locally and hard refresh (browser refresh button vs just hitting R to re-run), the theme reverts back to our standard theme vs restoring the theme sent with postMessage. This is intended behavior/unproblematic for SIS right?

@sfc-gh-jgarcia
Copy link
Contributor

Also when I test locally and hard refresh (browser refresh button vs just hitting R to re-run), the theme reverts back to our standard theme vs restoring the theme sent with postMessage. This is intended behavior/unproblematic for SIS right?

@sfc-gh-tteixeira or someone on the SiS team might be able to confirm with a bit more detail than me, but my understanding was that the way SiS infrastructure works, the app will be reloaded every time you access it, and we don't have any caching mechanism for themes there, so the message will always be re-sent, causing the app to update the theme accordingly (tough leading to other potential problems, such as flickering when the theme updates).

@sfc-gh-tteixeira
Copy link
Contributor Author

Also when I test locally and hard refresh (browser refresh button vs just hitting R to re-run), the theme reverts back to our standard theme vs restoring the theme sent with postMessage. This is intended behavior/unproblematic for SIS right?

The localStorage cache is not required for SiS. It would be great if it worked, but also OK if it doesn't. (And even if it worked, there are questions about when should we clear it. Do we do something smart there today, clearing based on the hash of the config or something?)

@vdonato
Copy link
Collaborator

vdonato commented Mar 31, 2023

Do we do something smart there today, clearing based on the hash of the config or something?

Ah, we do actually reapply the theme when the hash of the theming config received from the server changes (see the processThemeInput function in App.tsx), and this is probably what's breaking local theme caching with what we receive from postMessage since the server-defined theme will always be different.

Copy link
Collaborator

@vdonato vdonato left a comment

Choose a reason for hiding this comment

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

Proto changes LGTM

I also did a quick skim of the code itself and it looked fine to me, but I'm mostly relying on @mayagbarnes' review for an actual code review

Comment on lines +127 to +153
string widget_background_color = 7;
string widget_border_color = 8;
Radii radii = 9;
string body_font = 13;
string code_font = 14;
repeated FontFace font_faces = 15;
FontSizes font_sizes = 16;
}

message FontFace {
string url = 1;
string family = 2;
int32 weight = 3;
string style = 4;
}

message Radii {
// In pixels.
int32 baseWidgetRadius = 1;
int32 checkboxRadius = 2;
}

message FontSizes {
// In pixels.
int32 tinyFontSize = 1;
int32 smallFontSize = 2;
int32 baseFontSize = 3;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We'll need to give @sfc-gh-wschmitt a heads up that a bunch of fields are being added here, but I'm assuming this is okay because we probably just nuke the theme custom_theme field entirely when sanitizing forward messages

@sfc-gh-tteixeira
Copy link
Contributor Author

Ok, I'm seeing checkmarks next to both your names in Github, and looking at the discussion it feels like everything is resolved, so I'll just go ahead and merge.

Some future work:

  • Figure out caching. See this message from Vincent
  • Figure out a good behavior for when the theme is set by the user in config.toml
  • See if flicker from this solution is bad in practice. If so, implement solutions for that!

@sfc-gh-tteixeira sfc-gh-tteixeira merged commit b929c0e into develop Mar 31, 2023
@sfc-gh-tteixeira sfc-gh-tteixeira deleted the sis-updates branch March 31, 2023 19:12
tconkling added a commit that referenced this pull request Mar 31, 2023
* develop:
  Implement host->guest postMessage to update theme details (#6393)
headingFont: parsedFont,
bodyFont: themeInput.bodyFont ? themeInput.bodyFont : parsedFont,
headingFont: themeInput.bodyFont ? themeInput.bodyFont : parsedFont,
codeFont: themeInput.codeFont ? themeInput.codeFont : parsedFont,
Copy link
Contributor

Choose a reason for hiding this comment

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

@sfc-gh-tteixeira I am not sure if we want the same font in 3 places. This probably caused the regressions. See:
#6484

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants