diff --git a/frontend/src/metabase/core/components/Toggle/Toggle.styled.tsx b/frontend/src/metabase/core/components/Toggle/Toggle.styled.tsx index 51867738b37857576a2b0f66fc008fb1171504a6..1f14dacfd095d4d141994cca8b0c73e21c5febc8 100644 --- a/frontend/src/metabase/core/components/Toggle/Toggle.styled.tsx +++ b/frontend/src/metabase/core/components/Toggle/Toggle.styled.tsx @@ -41,6 +41,7 @@ export const ToggleRoot = styled.input<ToggleRootProps>` background-color: ${color("bg-medium")}; background-color: ${getBackgroundColor}; transition: background-color 0.3s; + flex-shrink: 0; &:after { content: ""; diff --git a/frontend/src/metabase/public/components/EmbedFrame.jsx b/frontend/src/metabase/public/components/EmbedFrame.jsx index 7c0dbfe45b04bfa546e3fb611de0769dc2c0bd41..f29f36b2040381aedd2b49bd36b8476ad2b13767 100644 --- a/frontend/src/metabase/public/components/EmbedFrame.jsx +++ b/frontend/src/metabase/public/components/EmbedFrame.jsx @@ -43,12 +43,13 @@ class EmbedFrame extends Component { } = this.props; const { innerScroll } = this.state; - const showFooter = !MetabaseSettings.hideEmbedBranding() || actionButtons; - - const { bordered, titled, theme, hide_parameters } = { + const { bordered, titled, theme, hide_parameters, hide_download_button } = { ...DEFAULT_OPTIONS, ...parseHashOptions(location.hash), }; + const showFooter = + !MetabaseSettings.hideEmbedBranding() || + (!hide_download_button && actionButtons); const name = titled ? this.props.name : null; diff --git a/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx b/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx index a134de60561398bdbab9d15553ed765c0b646679..30268dc1ec6f618e9a4ef19a19b7babba834c108 100644 --- a/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx +++ b/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.jsx @@ -6,6 +6,8 @@ import { connect } from "react-redux"; import Radio from "metabase/core/components/Radio/"; import CheckBox from "metabase/core/components/CheckBox"; import Select from "metabase/core/components/Select"; +import Toggle from "metabase/core/components/Toggle"; +import { useUniqueId } from "metabase/hooks/use-unique-id"; import MetabaseSettings from "metabase/lib/settings"; import { PLUGIN_SELECTORS } from "metabase/plugins"; import { t } from "ttag"; @@ -14,6 +16,8 @@ import { StyleContainer, DisplayOption, DisplayOptionTitle, + ToggleContainer, + ToggleLabel, } from "./DisplayOptionsPane.styled"; const THEME_OPTIONS = [ @@ -30,63 +34,88 @@ const DisplayOptionsPane = ({ displayOptions, onChangeDisplayOptions, canWhitelabel, -}) => ( - <div className={className}> - <DisplayOptionSection title={t`Style`}> - <StyleContainer> - <CheckBox - label={t`Border`} - checked={displayOptions.bordered} - onChange={e => - onChangeDisplayOptions({ - ...displayOptions, - bordered: e.target.checked, - }) - } - /> - <CheckBox - label={t`Title`} - checked={displayOptions.titled} - onChange={e => - onChangeDisplayOptions({ - ...displayOptions, - titled: e.target.checked, - }) +}) => { + const toggleId = useUniqueId("show-download-data-button"); + + return ( + <div className={className}> + <DisplayOptionSection title={t`Style`}> + <StyleContainer> + <CheckBox + label={t`Border`} + checked={displayOptions.bordered} + onChange={e => + onChangeDisplayOptions({ + ...displayOptions, + bordered: e.target.checked, + }) + } + /> + <CheckBox + label={t`Title`} + checked={displayOptions.titled} + onChange={e => + onChangeDisplayOptions({ + ...displayOptions, + titled: e.target.checked, + }) + } + /> + </StyleContainer> + </DisplayOptionSection> + <DisplayOptionSection title={t`Appearance`}> + <Radio + value={displayOptions.theme} + options={THEME_OPTIONS} + onChange={value => + onChangeDisplayOptions({ ...displayOptions, theme: value }) } - /> - </StyleContainer> - </DisplayOptionSection> - <DisplayOptionSection title={t`Appearance`}> - <Radio - value={displayOptions.theme} - options={THEME_OPTIONS} - onChange={value => - onChangeDisplayOptions({ ...displayOptions, theme: value }) - } - variant="normal" - showButtons - vertical - /> - </DisplayOptionSection> - {canWhitelabel && ( - <DisplayOptionSection title={t`Font`}> - <Select - value={displayOptions.font} - options={MetabaseSettings.get("available-fonts").map(font => ({ - name: font, - value: font, - }))} - onChange={e => { - onChangeDisplayOptions({ - ...displayOptions, - font: e.target.value, - }); - }} + variant="normal" + showButtons + vertical /> </DisplayOptionSection> - )} - </div> -); + {canWhitelabel && ( + <> + <DisplayOptionSection title={t`Font`}> + <Select + value={displayOptions.font} + options={MetabaseSettings.get("available-fonts").map(font => ({ + name: font, + value: font, + }))} + onChange={e => { + onChangeDisplayOptions({ + ...displayOptions, + font: e.target.value, + }); + }} + /> + </DisplayOptionSection> + <DisplayOptionSection title={t`Download data`}> + <ToggleContainer> + <ToggleLabel + htmlFor={toggleId} + >{t`Enable users to download data from this embed?`}</ToggleLabel> + <Toggle + id={toggleId} + aria-checked={!displayOptions.hide_download_button} + role="switch" + value={!displayOptions.hide_download_button} + onChange={isEnabled => { + onChangeDisplayOptions({ + ...displayOptions, + hide_download_button: !isEnabled ? true : null, + }); + }} + /> + </ToggleContainer> + </DisplayOptionSection> + </> + )} + </div> + ); +}; const DisplayOptionSection = ({ title, children }) => ( <DisplayOption> diff --git a/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.styled.jsx b/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.styled.jsx index 2da4d1d2064fb3cf8f3c9b8f53b87f9e6dcc0e90..28dc8626b20e6ddd5ef61d128ab6ac70569d9691 100644 --- a/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.styled.jsx +++ b/frontend/src/metabase/public/components/widgets/DisplayOptionsPane.styled.jsx @@ -1,4 +1,5 @@ import styled from "@emotion/styled"; +import { space } from "metabase/styled-components/theme"; export const StyleContainer = styled.div` display: flex; @@ -18,3 +19,13 @@ export const DisplayOption = styled.div` export const DisplayOptionTitle = styled.h3` margin-bottom: 1rem; `; + +export const ToggleContainer = styled.div` + display: flex; + align-items: center; +`; + +export const ToggleLabel = styled.label` + margin-right: ${space(3)}; + line-height: 1.5; +`; diff --git a/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js b/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js index 958565ce59ffa4f18786c7c433f2666b4d022a51..ce5c1be73a6498300eea342ec4b583bd30e35bc8 100644 --- a/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js +++ b/frontend/test/metabase/scenarios/embedding/embedding-snippets.cy.spec.js @@ -27,7 +27,17 @@ describe("scenarios > embedding > code snippets", () => { cy.get(".ace_content") .first() .invoke("text") - .should("match", JS_CODE(isEE)); + .should("match", JS_CODE({ isEE })); + + // hide download button for pro/enterprise users metabase#23477 + cy.findByLabelText( + "Enable users to download data from this embed?", + ).click(); + + cy.get(".ace_content") + .first() + .invoke("text") + .should("match", JS_CODE({ isEE, hideDownloadButton: true })); cy.get(".ace_content") .last() diff --git a/frontend/test/metabase/scenarios/embedding/embedding-snippets.js b/frontend/test/metabase/scenarios/embedding/embedding-snippets.js index 04e89176c68e404465cb31733110d73e2f6eb258..237239cfd8f1f579341fc9b007d8017f485d10fa 100644 --- a/frontend/test/metabase/scenarios/embedding/embedding-snippets.js +++ b/frontend/test/metabase/scenarios/embedding/embedding-snippets.js @@ -1,4 +1,4 @@ -export const JS_CODE = isEE => +export const JS_CODE = ({ isEE, hideDownloadButton }) => new RegExp( `// you will need to install via 'npm install jsonwebtoken' or in your package.json @@ -13,9 +13,9 @@ var payload = { }; var token = jwt.sign(payload, METABASE_SECRET_KEY); -var iframeUrl = METABASE_SITE_URL + "/embed/dashboard/" + token + "#bordered=true&titled=true${ - isEE ? "&font=Lato" : "" - }";` +var iframeUrl = METABASE_SITE_URL + "/embed/dashboard/" + token + "#bordered=true&titled=true${getParameter( + { isEE, hideDownloadButton }, + )}";` .split("\n") .join("") .replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") @@ -32,3 +32,17 @@ export const IFRAME_CODE = `<iframe ></iframe>` .split("\n") .join(""); + +function getParameter({ isEE, hideDownloadButton }) { + let parameter = ""; + + if (isEE) { + parameter += "&font=Lato"; + } + + if (hideDownloadButton) { + parameter += "&hide_download_button=true"; + } + + return parameter; +}