Skip to content

Commit

Permalink
Download button host config (streamlit#8584)
Browse files Browse the repository at this point in the history
Add `enforceDownloadInNewTab` host config to manage download_button
  • Loading branch information
kajarenc authored May 10, 2024
1 parent b48260b commit 92e41ac
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 7 deletions.
7 changes: 6 additions & 1 deletion frontend/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -409,14 +409,19 @@ export class App extends PureComponent<Props, State> {
disableFullscreenMode,
enableCustomParentMessages,
mapboxToken,
enforceDownloadInNewTab,
} = response

const appConfig: AppConfig = {
allowedOrigins,
useExternalAuthToken,
enableCustomParentMessages,
}
const libConfig: LibConfig = { mapboxToken, disableFullscreenMode }
const libConfig: LibConfig = {
mapboxToken,
disableFullscreenMode,
enforceDownloadInNewTab,
}

// Set the allowed origins configuration for the host communication:
this.hostCommunicationMgr.setAllowedOrigins(appConfig)
Expand Down
2 changes: 2 additions & 0 deletions frontend/lib/src/components/core/LibContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export type LibConfig = {
* Whether to disable the full screen mode all elements / widgets.
*/
disableFullscreenMode?: boolean

enforceDownloadInNewTab?: boolean
}

export interface LibContextProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { WidgetStateManager } from "@streamlit/lib/src/WidgetStateManager"

import { DownloadButton as DownloadButtonProto } from "@streamlit/lib/src/proto"
import { mockEndpoints } from "@streamlit/lib/src/mocks/mocks"
import DownloadButton, { Props } from "./DownloadButton"
import DownloadButton, { Props, createDownloadLink } from "./DownloadButton"

jest.mock("@streamlit/lib/src/WidgetStateManager")
jest.mock("@streamlit/lib/src/StreamlitEndpoints")
Expand Down Expand Up @@ -98,6 +98,23 @@ describe("DownloadButton widget", () => {
)
})

it("has a correct new tab behaviour download link", () => {
const props = getProps()
const sameTabLink = createDownloadLink(
props.endpoints,
props.element.url,
false
)
expect(sameTabLink.getAttribute("target")).toBe("_self")

const newTabLink = createDownloadLink(
props.endpoints,
props.element.url,
true
)
expect(newTabLink.getAttribute("target")).toBe("_blank")
})

it("can set fragmentId on click", () => {
const props = getProps(undefined, { fragmentId: "myFragmentId" })
render(<DownloadButton {...props} />)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import BaseButton, {
import { WidgetStateManager } from "@streamlit/lib/src/WidgetStateManager"
import StreamlitMarkdown from "@streamlit/lib/src/components/shared/StreamlitMarkdown"
import { StreamlitEndpoints } from "@streamlit/lib/src/StreamlitEndpoints"
import { LibContext } from "@streamlit/lib/src/components/core/LibContext"

export interface Props {
endpoints: StreamlitEndpoints
Expand All @@ -34,9 +35,29 @@ export interface Props {
fragmentId?: string
}

export function createDownloadLink(
endpoints: StreamlitEndpoints,
url: string,
enforceDownloadInNewTab: boolean
): HTMLAnchorElement {
const link = document.createElement("a")
const uri = endpoints.buildMediaURL(url)
link.setAttribute("href", uri)
if (enforceDownloadInNewTab) {
link.setAttribute("target", "_blank")
} else {
link.setAttribute("target", "_self")
}
link.setAttribute("download", "")
return link
}

function DownloadButton(props: Props): ReactElement {
const { disabled, element, widgetMgr, width, endpoints, fragmentId } = props
const style = { width }
const {
libConfig: { enforceDownloadInNewTab = false }, // Default to false, if no libConfig, e.g. for tests
} = React.useContext(LibContext)

const kind =
element.type === "primary"
Expand All @@ -47,11 +68,11 @@ function DownloadButton(props: Props): ReactElement {
// Downloads are only done on links, so create a hidden one and click it
// for the user.
widgetMgr.setTriggerValue(element, { fromUi: true }, fragmentId)
const link = document.createElement("a")
const uri = endpoints.buildMediaURL(element.url)
link.setAttribute("href", uri)
link.setAttribute("target", "_self")
link.setAttribute("download", "")
const link = createDownloadLink(
endpoints,
element.url,
enforceDownloadInNewTab
)
link.click()
}

Expand Down
1 change: 1 addition & 0 deletions lib/streamlit/web/server/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ async def get(self) -> None:
"useExternalAuthToken": False,
# Default host configuration settings.
"enableCustomParentMessages": False,
"enforceDownloadInNewTab": False,
}
)
self.set_status(200)
Expand Down
1 change: 1 addition & 0 deletions lib/tests/streamlit/web/server/routes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ def test_allowed_message_origins(self):
"useExternalAuthToken": False,
# Default host configuration settings:
"enableCustomParentMessages": False,
"enforceDownloadInNewTab": False,
},
response_body,
)
Expand Down

0 comments on commit 92e41ac

Please sign in to comment.