feat(embeds): various changes (#10597)

* upgrade storybook

* flip the "legend" option, enable by default on png exports

* noHeader sharing mode for insights

* fix storybook 6.5 issue

* split exporter component and rendering

* remove `fitScreen` from options - it was not used anywhere from what I can tell

* storybook with no data

* get an actual iframe into storybook

* export insight

* dashboard mock

* load dashboard directly, not via window

* add dashboard story

* tweaks

* code fixes

* fix type
This commit is contained in:
Marius Andra
2022-07-04 10:02:52 +02:00
committed by GitHub
parent f35a964b6e
commit 16e62f1004
45 changed files with 2942 additions and 947 deletions

View File

@@ -34,7 +34,7 @@ await buildInParallel(
},
{
name: 'Exporter',
entryPoints: ['src/exporter/Exporter.tsx'],
entryPoints: ['src/exporter/index.tsx'],
format: 'iife',
outfile: path.resolve(__dirname, 'dist', 'exporter.js'),
...common,
@@ -76,5 +76,5 @@ export function writeIndexHtml(chunks = {}, entrypoints = []) {
}
export function writeExporterHtml(chunks = {}, entrypoints = []) {
copyIndexHtml(__dirname, 'src/exporter/exporter.html', 'dist/exporter.html', 'exporter', chunks, entrypoints)
copyIndexHtml(__dirname, 'src/exporter/index.html', 'dist/exporter.html', 'exporter', chunks, entrypoints)
}

View File

@@ -9,21 +9,22 @@
display: flex;
flex-direction: column;
.ExportedInsight-header {
.ExportedInsight__header {
padding: 1rem;
border-bottom: 1px solid var(--border);
display: flex;
justify-content: space-between;
.ExportedInsight-header-title {
.ExportedInsight__header__title {
font-size: 1rem;
font-weight: 600;
}
}
.ExportedInsight-content {
.ExportedInsight__content {
flex: 1;
position: relative;
z-index: 1;
.FunnelBarChart {
min-height: 50vw;
}
@@ -39,11 +40,22 @@
}
}
.ExportedInsight__watermark {
position: absolute;
right: 0;
top: 0;
z-index: 2;
svg {
font-size: 0.75rem;
margin: 0.25rem;
}
}
&.ExportedInsight--fit-screen {
height: 100vh;
max-height: 100vh;
.ExportedInsight-content {
.ExportedInsight__content {
display: flex;
flex-direction: column;
@@ -59,6 +71,10 @@
min-height: 100px;
max-height: 100%;
}
&.ExportedInsight__content--with-watermark {
padding-top: 1rem;
}
}
}
}

View File

@@ -8,15 +8,17 @@ import { INSIGHT_TYPES_METADATA } from 'scenes/saved-insights/SavedInsights'
import { dateFilterToText } from 'lib/utils'
import { FriendlyLogo } from '~/toolbar/assets/FriendlyLogo'
import { InsightLegend } from 'lib/components/InsightLegend/InsightLegend'
import { ExportOptions } from '~/exporter/types'
import { ExportOptions, ExportType } from '~/exporter/types'
import clsx from 'clsx'
export function ExportedInsight({
insight,
exportOptions: { whitelabel, noLegend, fitScreen },
exportOptions: { whitelabel, noHeader, legend },
type,
}: {
insight: InsightModel
exportOptions: ExportOptions
type: ExportType
}): JSX.Element {
const insightLogicProps: InsightLogicProps = {
dashboardItemId: insight.short_id,
@@ -27,36 +29,51 @@ export function ExportedInsight({
const { filters, name, derived_name, description } = insight
const showLegend =
!noLegend &&
legend &&
filters.insight === InsightType.TRENDS &&
filters.display !== ChartDisplayType.WorldMap &&
filters.display !== ChartDisplayType.ActionsTable
const showWatermark = noHeader && !whitelabel
return (
<BindLogic logic={insightLogic} props={insightLogicProps}>
<div
className={clsx('ExportedInsight', {
'ExportedInsight--fit-screen': fitScreen,
'ExportedInsight--fit-screen': type === ExportType.Embed,
})}
>
<div className="ExportedInsight-header">
<div>
<h5>
<span title={INSIGHT_TYPES_METADATA[filters.insight || InsightType.TRENDS]?.description}>
{INSIGHT_TYPES_METADATA[filters.insight || InsightType.TRENDS]?.name}
</span>{' '}
{dateFilterToText(filters.date_from, filters.date_to, 'Last 7 days')}
</h5>
<h4 title={name} className="ExportedInsight-header-title">
{name || derived_name}
</h4>
{description && <div className="ExportedInsight-header-description">{description}</div>}
{!noHeader && (
<div className="ExportedInsight__header">
<div>
<h5>
<span
title={INSIGHT_TYPES_METADATA[filters.insight || InsightType.TRENDS]?.description}
>
{INSIGHT_TYPES_METADATA[filters.insight || InsightType.TRENDS]?.name}
</span>{' '}
{dateFilterToText(filters.date_from, filters.date_to, 'Last 7 days')}
</h5>
<h4 title={name} className="ExportedInsight__header__title">
{name || derived_name}
</h4>
{description && <div className="ExportedInsight__header-description">{description}</div>}
</div>
{!whitelabel && <FriendlyLogo style={{ fontSize: '1rem' }} />}
</div>
)}
{showWatermark && (
<div className="ExportedInsight__watermark">
<FriendlyLogo />
</div>
)}
{!whitelabel && <FriendlyLogo style={{ fontSize: '1rem' }} />}
</div>
<div className="ExportedInsight-content">
<div
className={clsx({
ExportedInsight__content: true,
'ExportedInsight__content--with-watermark': showWatermark,
})}
>
<InsightViz insight={insight as any} style={{ top: 0, left: 0, position: 'relative' }} />
{showLegend ? (
<div className="pa">

View File

@@ -0,0 +1,41 @@
import React, { useEffect } from 'react'
import { ComponentMeta, ComponentStory } from '@storybook/react'
import { Exporter } from './Exporter'
import { insight, dashboard } from '~/exporter/__mocks__/Exporter.mocks'
export default {
title: 'Exporter/Exporter',
component: Exporter,
args: {
type: 'embed',
whitelabel: false,
noHeader: false,
legend: false,
},
parameters: {
docs: {
inlineStories: false,
iframeHeight: 400,
source: { state: 'close' },
},
viewMode: 'story',
},
} as ComponentMeta<typeof Exporter>
const Template: ComponentStory<typeof Exporter> = (props) => {
useEffect(() => {
document.body.className = ''
document.documentElement.className = `export-type-${props.type}`
}, [props.type])
return (
<div className={`storybook-export-type-${props.type}`}>
<Exporter {...props} />
</div>
)
}
export const Insight = Template.bind({})
Insight.args = { insight }
export const Dashboard = Template.bind({})
Dashboard.args = { dashboard }

View File

@@ -1,31 +1,16 @@
import '~/styles'
import './Exporter.scss'
import React from 'react'
import ReactDOM from 'react-dom'
import { loadPostHogJS } from '~/loadPostHogJS'
import { initKea } from '~/initKea'
import { ExportedData, ExportType } from '~/exporter/types'
import { DashboardPlacement } from '~/types'
import { ExportedInsight } from '~/exporter/ExportedInsight/ExportedInsight'
import { FriendlyLogo } from '~/toolbar/assets/FriendlyLogo'
import { Dashboard } from 'scenes/dashboard/Dashboard'
const exportedData: ExportedData = window.POSTHOG_EXPORTED_DATA
// Disable tracking for all exports and embeds.
// This is explicitly set as to not track our customers' customers data.
// Without it, embeds of self-hosted iframes will log metrics to app.posthog.com.
window.JS_POSTHOG_API_KEY = null
loadPostHogJS()
initKea()
function Exporter(): JSX.Element {
const { type, dashboard, insight, team, ...exportOptions } = exportedData
export function Exporter(props: ExportedData): JSX.Element {
const { type, dashboard, insight, team, ...exportOptions } = props
const { whitelabel } = exportOptions
exportOptions.fitScreen = exportOptions.fitScreen ?? type == ExportType.Embed
return (
<div className="Exporter">
{!whitelabel && dashboard ? (
@@ -60,10 +45,11 @@ function Exporter(): JSX.Element {
) : null}
{insight ? (
<ExportedInsight insight={insight} exportOptions={exportOptions} />
<ExportedInsight type={type} insight={insight} exportOptions={exportOptions} />
) : dashboard ? (
<Dashboard
id={String(dashboard.id)}
dashboard={dashboard}
placement={type === ExportType.Image ? DashboardPlacement.Export : DashboardPlacement.Public}
/>
) : (
@@ -88,5 +74,3 @@ function Exporter(): JSX.Element {
</div>
)
}
ReactDOM.render(<Exporter />, document.getElementById('root'))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
import '~/styles'
import './Exporter.scss'
import React from 'react'
import ReactDOM from 'react-dom'
import { loadPostHogJS } from '~/loadPostHogJS'
import { initKea } from '~/initKea'
import { Exporter } from '~/exporter/Exporter'
import { ExportedData } from '~/exporter/types'
// Disable tracking for all exports and embeds.
// This is explicitly set as to not track our customers' customers data.
// Without it, embeds of self-hosted iframes will log metrics to app.posthog.com.
window.JS_POSTHOG_API_KEY = null
loadPostHogJS()
initKea()
const exportedData: ExportedData = window.POSTHOG_EXPORTED_DATA
ReactDOM.render(<Exporter {...exportedData} />, document.getElementById('root'))

View File

@@ -8,13 +8,13 @@ export enum ExportType {
export interface ExportOptions {
whitelabel?: boolean
noLegend?: boolean
fitScreen?: boolean
noHeader?: boolean
legend?: boolean
}
export interface ExportedData extends ExportOptions {
type: ExportType
dashboard?: Partial<DashboardType>
dashboard?: DashboardType
insight?: InsightModel
team?: Partial<TeamType>
}

View File

@@ -6,7 +6,7 @@ import { SideBar } from './SideBar/SideBar'
export default {
title: 'Layout/Navigation',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export function Navigation_(): JSX.Element {

View File

@@ -15,7 +15,7 @@ const fakeInsight: Partial<InsightModel> = {
export default {
title: 'Components/Sharing',
component: Sharing,
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as ComponentMeta<typeof Sharing>
const Template = (args: Partial<SharingModalProps> & { licensed?: boolean }): JSX.Element => {

View File

@@ -42,7 +42,7 @@ export function Sharing({ dashboardId, insightShortId, insight, closeModal }: Sh
const [iframeLoaded, setIframeLoaded] = useState(false)
const showNoLegendCheckbox = insight?.filters?.insight === InsightType.TRENDS
const showLegendCheckbox = insight?.filters?.insight === InsightType.TRENDS
const resource = dashboardId ? 'dashboard' : 'insight'
useEffect(() => {
@@ -152,15 +152,28 @@ export function Sharing({ dashboardId, insightShortId, insight, closeModal }: Sh
/>
)}
</Field>
{showNoLegendCheckbox && (
<Field name="noLegend" noStyle>
{insight && (
<Field name="noHeader" noStyle>
{({ value, onChange }) => (
<LemonSwitch
fullWidth
type="primary"
label={<div>Show title and description</div>}
onChange={() => onChange(!value)}
checked={!value}
/>
)}
</Field>
)}
{showLegendCheckbox && (
<Field name="legend" noStyle>
{({ value, onChange }) => (
<LemonSwitch
fullWidth
type="primary"
label={<div>Show legend</div>}
onChange={() => onChange(!value)}
checked={!value}
checked={value}
/>
)}
</Field>

View File

@@ -28,7 +28,8 @@ const defaultEmbedConfig: EmbedConfig = {
width: '100%',
height: '400',
whitelabel: false,
noLegend: false,
legend: false,
noHeader: false,
}
const propsToApiParams = async (props: SharingLogicProps): Promise<{ dashboardId?: number; insightId?: number }> => {
@@ -87,16 +88,16 @@ export const sharingLogic = kea<sharingLogicType>([
],
shareLink: [
(s) => [s.siteUrl, s.sharingConfiguration, s.embedConfig],
(siteUrl, sharingConfiguration, { whitelabel, noLegend }) =>
(siteUrl, sharingConfiguration, { whitelabel, legend, noHeader }) =>
sharingConfiguration
? siteUrl + urls.shared(sharingConfiguration.access_token, { whitelabel, noLegend })
? siteUrl + urls.shared(sharingConfiguration.access_token, { whitelabel, legend, noHeader })
: '',
],
embedLink: [
(s) => [s.siteUrl, s.sharingConfiguration, s.embedConfig],
(siteUrl, sharingConfiguration, { whitelabel, noLegend }) =>
(siteUrl, sharingConfiguration, { whitelabel, legend, noHeader }) =>
sharingConfiguration
? siteUrl + urls.embedded(sharingConfiguration.access_token, { whitelabel, noLegend })
? siteUrl + urls.embedded(sharingConfiguration.access_token, { whitelabel, legend, noHeader })
: '',
],
iframeProperties: [

View File

@@ -13,7 +13,7 @@ import { createMockSubscription, mockIntegration, mockSlackChannels } from '~/te
export default {
title: 'Components/Subscriptions',
component: Subscriptions,
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as ComponentMeta<typeof Subscriptions>
const Template = (

View File

@@ -1088,7 +1088,7 @@ export function endWithPunctation(text?: string | null): string {
return trimmedText
}
export function shortTimeZone(timeZone?: string, atDate?: Date): string | null {
export function shortTimeZone(timeZone?: string | null, atDate?: Date): string | null {
/**
* Return the short timezone identifier for a specific timezone (e.g. BST, EST, PDT, UTC+2).
* @param timeZone E.g. 'America/New_York'

View File

@@ -5,7 +5,7 @@ import React from 'react'
export default {
title: 'Scenes-Other/Onboarding',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const Preflight = (): JSX.Element => <PreflightCheck />

View File

@@ -5,7 +5,7 @@ import React from 'react'
export default {
title: 'Scenes-Other/Unsubscribe',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const UnsubscribeScene = (): JSX.Element => <Unsubscribe />

View File

@@ -9,7 +9,7 @@ import { urls } from 'scenes/urls'
export default {
title: 'Scenes-Other/Login',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
// export more stories with different state

View File

@@ -9,7 +9,7 @@ import { passwordResetLogic } from 'scenes/authentication/passwordResetLogic'
// some metadata and optional parameters
export default {
title: 'Scenes-Other/Password Reset',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
// export more stories with different state

View File

@@ -9,7 +9,7 @@ import { useStorybookMocks } from '~/mocks/browser'
// some metadata and optional parameters
export default {
title: 'Scenes-Other/Password Reset Complete',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
// export more stories with different state

View File

@@ -8,7 +8,7 @@ import preflightJson from '~/mocks/fixtures/_preflight.json'
export default {
title: 'Scenes-Other/Signup',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
decorators: [
mswDecorator({
get: { '/api/users/@me': () => [500, null] },

View File

@@ -8,7 +8,7 @@ import { urls } from 'scenes/urls'
export default {
title: 'Scenes-Other/Billing',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
decorators: [
mswDecorator({
get: {

View File

@@ -21,7 +21,7 @@ export default {
},
}),
],
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const List = (): JSX.Element => {

View File

@@ -541,11 +541,7 @@ export const dashboardLogic = kea<dashboardLogicType>({
events: ({ actions, cache, props }) => ({
afterMount: () => {
if (props.id) {
const exportedDashboard = window.POSTHOG_EXPORTED_DATA?.dashboard
if (exportedDashboard && exportedDashboard.id === props.id && exportedDashboard.items) {
actions.loadExportedDashboard(exportedDashboard as DashboardType)
} else if (props.dashboard) {
// When the scene is initially loaded, the dashboard ID is undefined
if (props.dashboard) {
actions.loadExportedDashboard(props.dashboard)
} else {
actions.loadDashboardItems({

View File

@@ -14,7 +14,7 @@ export default {
get: { '/api/projects/:projectId/events': { next: null, results: eventList } },
}),
],
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const LiveEvents = (): JSX.Element => {

View File

@@ -10,7 +10,7 @@ import { AvailableFeature } from '~/types'
export default {
title: 'Scenes-App/Feature Flags',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' }, // scene mode
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' }, // scene mode
decorators: [
mswDecorator({
get: {

View File

@@ -6,7 +6,7 @@ import React from 'react'
export default {
title: 'Scenes-Other/Onboarding',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const Ingestion = (): JSX.Element => <IngestionWizard />

View File

@@ -12,7 +12,7 @@ import { App } from 'scenes/App'
// some metadata and optional parameters
export default {
title: 'Scenes-App/Insights/Error states',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export function EmptyState(): JSX.Element {

View File

@@ -45,7 +45,7 @@ export interface InsightTooltipProps extends TooltipConfig {
seriesData?: SeriesDatum[]
forceEntitiesAsColumns?: boolean
groupTypeLabel?: string
timezone?: string
timezone?: string | null
}
export const COL_CUTOFF = 4

View File

@@ -5,7 +5,7 @@ import { createInsightScene } from 'scenes/insights/__mocks__/createInsightScene
export default {
title: 'Scenes-App/Insights',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
decorators: [
mswDecorator({
get: {

View File

@@ -59,7 +59,7 @@ interface LineGraphProps {
isCompare?: boolean
incompletenessOffsetFromEnd?: number // Number of data points at end of dataset to replace with a dotted line. Only used in line graphs.
labelGroupType: number | 'people' | 'none'
timezone?: string
timezone?: string | null
}
const noop = (): void => {}

View File

@@ -6,7 +6,7 @@ import { urls } from 'scenes/urls'
export default {
title: 'Scenes-App/Licenses',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const Licenses = (): JSX.Element => {

View File

@@ -12,7 +12,7 @@ import { PerfBlock } from 'scenes/performance/WebPerformanceWaterfallChart'
export default {
title: 'Scenes-App/Web Performance',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
decorators: [
mswDecorator({
get: {

View File

@@ -16,7 +16,7 @@ export default {
},
}),
],
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
export const ProjectHomepage = (): JSX.Element => {

View File

@@ -10,7 +10,7 @@ import { SlackIntegration } from './SlackIntegration'
export default {
title: 'Components/Integrations/Slack',
component: SlackIntegration,
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as ComponentMeta<typeof SlackIntegration>
const Template = (args: { instanceConfigured?: boolean; integrated?: boolean }): JSX.Element => {

View File

@@ -15,7 +15,7 @@ const insights = [trendsBarBreakdown, trendsPieBreakdown, funnelTopToBottom]
export default {
title: 'Scenes-App/Saved Insights',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
decorators: [
mswDecorator({
get: {

View File

@@ -8,7 +8,7 @@ import { App } from 'scenes/App'
export default {
title: 'Scenes-App/Recordings',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
decorators: [
mswDecorator({
get: {

View File

@@ -92,11 +92,13 @@ export const urls = {
shared: (token: string, exportOptions?: ExportOptions): string =>
combineUrl(`/shared/${token}`, {
...(exportOptions?.whitelabel ? { whitelabel: null } : {}),
...(exportOptions?.noLegend ? { noLegend: null } : {}),
...(exportOptions?.legend ? { legend: null } : {}),
...(exportOptions?.noHeader ? { legend: null } : {}),
}).url,
embedded: (token: string, exportOptions?: ExportOptions): string =>
combineUrl(`/embedded/${token}`, {
...(exportOptions?.whitelabel ? { whitelabel: null } : {}),
...(exportOptions?.noLegend ? { noLegend: null } : {}),
...(exportOptions?.legend ? { legend: null } : {}),
...(exportOptions?.noHeader ? { noHeader: null } : {}),
}).url,
}

View File

@@ -184,7 +184,7 @@ export default {
}),
],
// NB! These `parameters` only apply for Scene stories.
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' }, // scene mode
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' }, // scene mode
} as Meta
export function NewDashboard (): JSX.Element {

View File

@@ -21,7 +21,7 @@ const editorParams: EditorProps = {
export default {
title: 'Scenes-Other/Toolbar',
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'canvas' },
parameters: { layout: 'fullscreen', options: { showPanel: false }, viewMode: 'story' },
} as Meta
function useToolbarStyles(): void {

View File

@@ -797,7 +797,7 @@ export interface InsightModel extends DashboardTile {
/** The primary key in the database, used as well in API endpoints */
id: number
name: string
derived_name?: string
derived_name?: string | null
description?: string
favorited?: boolean
order: number | null
@@ -814,7 +814,7 @@ export interface InsightModel extends DashboardTile {
last_modified_by: UserBasicType | null
effective_restriction_level: DashboardRestrictionLevel
effective_privilege_level: DashboardPrivilegeLevel
timezone?: string
timezone?: string | null
/** Only used in the frontend to store the next breakdown url */
next?: string
}

View File

@@ -123,11 +123,11 @@
"@babel/preset-typescript": "^7.16.7",
"@cypress/webpack-preprocessor": "5.12.0",
"@hot-loader/react-dom": "^16.13.0",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/addon-storysource": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/addon-actions": "^6.5.9",
"@storybook/addon-essentials": "^6.5.9",
"@storybook/addon-links": "^6.5.9",
"@storybook/addon-storysource": "^6.5.9",
"@storybook/react": "^6.5.9",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
@@ -191,7 +191,7 @@
"prettier": "^2.3.1",
"raw-loader": "^4.0.2",
"sass-loader": "^10.0.1",
"storybook-addon-turbo-build": "^1.0.1",
"storybook-addon-turbo-build": "^1.1.0",
"style-loader": "^2.0.0",
"timekeeper": "^2.2.0",
"ts-node": "^9.1.1",

View File

@@ -185,8 +185,10 @@ class SharingViewerPageViewSet(mixins.RetrieveModelMixin, StructuredViewSetMixin
if "whitelabel" in request.GET and "white_labelling" in resource.team.organization.available_features:
exported_data.update({"whitelabel": True})
if "noLegend" in request.GET:
exported_data.update({"noLegend": True})
if "noHeader" in request.GET:
exported_data.update({"noHeader": True})
if "legend" in request.GET:
exported_data.update({"legend": True})
if request.path.endswith(f".json"):
return response.Response(exported_data)

View File

@@ -77,7 +77,7 @@ def _export_to_png(exported_asset: ExportedAsset) -> None:
access_token = get_public_access_token(exported_asset, timedelta(minutes=15))
if exported_asset.insight is not None:
url_to_render = absolute_uri(f"/exporter?token={access_token}")
url_to_render = absolute_uri(f"/exporter?token={access_token}&legend")
wait_for_css_selector = ".ExportedInsight"
screenshot_width = 800
elif exported_asset.dashboard is not None:

2170
yarn.lock

File diff suppressed because it is too large Load Diff