-
Notifications
You must be signed in to change notification settings - Fork 5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Embedding SDK - integration commit (#40198)
* Minimal SDK code * Fix viz, styles * Move SDK code to enterprise folder * Fix files structure * Clean-up * Review fixes * Review fixes * Review fixes * Remove elementid * Reuse some options from main webpack config * Actualize package.json * Actualize package.json
- Loading branch information
1 parent
65e527c
commit 98f6cd8
Showing
28 changed files
with
891 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# Embedding SDK | ||
|
||
### Build | ||
`yarn build` | ||
|
||
`yarn build-embedding-sdk` | ||
|
||
Build results are located at `<root>/resources/embedding-sdk` |
36 changes: 36 additions & 0 deletions
36
enterprise/frontend/src/embedding-sdk/components/private/AppInitializeController.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import type * as React from "react"; | ||
import { t } from "ttag"; | ||
|
||
import { DEFAULT_FONT } from "../../config"; | ||
import { EmbeddingContext } from "../../context"; | ||
import { useInitData } from "../../hooks"; | ||
import type { SDKConfigType } from "../../types"; | ||
|
||
import { SdkContentWrapper } from "./SdkContentWrapper"; | ||
|
||
interface AppInitializeControllerProps { | ||
children: React.ReactNode; | ||
config: SDKConfigType; | ||
} | ||
|
||
export const AppInitializeController = ({ | ||
config, | ||
children, | ||
}: AppInitializeControllerProps) => { | ||
const { isLoggedIn, isInitialized } = useInitData({ | ||
config, | ||
}); | ||
|
||
return ( | ||
<EmbeddingContext.Provider | ||
value={{ | ||
isInitialized, | ||
isLoggedIn, | ||
}} | ||
> | ||
<SdkContentWrapper font={config.font ?? DEFAULT_FONT}> | ||
{!isInitialized ? <div>{t`Loading…`}</div> : children} | ||
</SdkContentWrapper> | ||
</EmbeddingContext.Provider> | ||
); | ||
}; |
34 changes: 34 additions & 0 deletions
34
enterprise/frontend/src/embedding-sdk/components/private/SdkContentWrapper.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import styled from "@emotion/styled"; | ||
|
||
import { alpha, color } from "metabase/lib/colors"; | ||
import { aceEditorStyles } from "metabase/query_builder/components/NativeQueryEditor/NativeQueryEditor.styled"; | ||
import { saveDomImageStyles } from "metabase/visualizations/lib/save-chart-image"; | ||
|
||
export const SdkContentWrapper = styled.div<{ font: string }>` | ||
--default-font-family: "${({ font }) => font}"; | ||
--color-brand: ${color("brand")}; | ||
--color-brand-alpha-04: ${alpha("brand", 0.04)}; | ||
--color-brand-alpha-88: ${alpha("brand", 0.88)}; | ||
--color-focus: ${color("focus")}; | ||
${aceEditorStyles} | ||
${saveDomImageStyles} | ||
--default-font-size: 0.875em; | ||
--default-font-color: var(--color-text-dark); | ||
--default-bg-color: var(--color-bg-light); | ||
font-family: var(--default-font-family), sans-serif; | ||
font-size: var(--default-font-size); | ||
font-weight: 400; | ||
font-style: normal; | ||
color: var(--color-text-dark); | ||
margin: 0; | ||
height: 100%; /* ensure the entire page will fill the window */ | ||
display: flex; | ||
flex-direction: column; | ||
background-color: var(--color-bg-light); | ||
-webkit-font-smoothing: antialiased; | ||
-moz-osx-font-smoothing: grayscale; | ||
`; |
40 changes: 40 additions & 0 deletions
40
enterprise/frontend/src/embedding-sdk/components/public/MetabaseProvider.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { AnyAction, Store } from "@reduxjs/toolkit"; | ||
import type * as React from "react"; | ||
import { memo } from "react"; | ||
import { Provider } from "react-redux"; | ||
|
||
import reducers from "metabase/reducers-main"; | ||
import { getStore } from "metabase/store"; | ||
import { EmotionCacheProvider } from "metabase/styled-components/components/EmotionCacheProvider"; | ||
import { ThemeProvider } from "metabase/ui/components/theme/ThemeProvider"; | ||
import type { State } from "metabase-types/store"; | ||
|
||
import type { SDKConfigType } from "../../types"; | ||
import { AppInitializeController } from "../private/AppInitializeController"; | ||
|
||
import "metabase/css/vendor.css"; | ||
import "metabase/css/index.module.css"; | ||
|
||
const store = getStore(reducers) as unknown as Store<State, AnyAction>; | ||
|
||
const MetabaseProviderInternal = ({ | ||
children, | ||
config, | ||
}: { | ||
children: React.ReactNode; | ||
config: SDKConfigType; | ||
}): React.JSX.Element => { | ||
return ( | ||
<Provider store={store}> | ||
<EmotionCacheProvider> | ||
<ThemeProvider> | ||
<AppInitializeController config={config}> | ||
{children} | ||
</AppInitializeController> | ||
</ThemeProvider> | ||
</EmotionCacheProvider> | ||
</Provider> | ||
); | ||
}; | ||
|
||
export const MetabaseProvider = memo(MetabaseProviderInternal); |
165 changes: 165 additions & 0 deletions
165
enterprise/frontend/src/embedding-sdk/components/public/QueryVisualization.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { useEffect, useState } from "react"; | ||
|
||
import LoadingAndErrorWrapper from "metabase/components/LoadingAndErrorWrapper"; | ||
import { useSelector } from "metabase/lib/redux"; | ||
import { | ||
onCloseChartType, | ||
onOpenChartSettings, | ||
setUIControls, | ||
} from "metabase/query_builder/actions"; | ||
import QueryVisualization from "metabase/query_builder/components/QueryVisualization"; | ||
import ChartTypeSidebar from "metabase/query_builder/components/view/sidebars/ChartTypeSidebar"; | ||
import { getMetadata } from "metabase/selectors/metadata"; | ||
import { CardApi } from "metabase/services"; | ||
import { Box, Group, Text } from "metabase/ui"; | ||
import { PublicMode } from "metabase/visualizations/click-actions/modes/PublicMode"; | ||
import Question from "metabase-lib/v1/Question"; | ||
import type { Card, CardId, Dataset } from "metabase-types/api"; | ||
|
||
import { useEmbeddingContext } from "../../context"; | ||
|
||
interface QueryVisualizationProps { | ||
questionId: CardId; | ||
showVisualizationSelector?: boolean; | ||
} | ||
|
||
type State = { | ||
loading: boolean; | ||
card: Card | null; | ||
cardError?: Card | string | null; | ||
result: Dataset | null; | ||
resultError?: Dataset | string | null; | ||
}; | ||
|
||
export const QueryVisualizationSdk = ({ | ||
questionId, | ||
showVisualizationSelector, | ||
}: QueryVisualizationProps): JSX.Element | null => { | ||
const { isInitialized, isLoggedIn } = useEmbeddingContext(); | ||
const metadata = useSelector(getMetadata); | ||
|
||
const [{ loading, card, result, cardError, resultError }, setState] = | ||
useState<State>({ | ||
loading: false, | ||
card: null, | ||
cardError: null, | ||
result: null, | ||
resultError: null, | ||
}); | ||
|
||
const loadCardData = async ({ questionId }: { questionId: number }) => { | ||
setState(prevState => ({ | ||
...prevState, | ||
loading: true, | ||
})); | ||
|
||
Promise.all([ | ||
CardApi.get({ cardId: questionId }), | ||
CardApi.query({ | ||
cardId: questionId, | ||
}), | ||
]) | ||
.then(([card, result]) => { | ||
setState(prevState => ({ | ||
...prevState, | ||
card, | ||
result, | ||
loading: false, | ||
cardError: null, | ||
resultError: null, | ||
})); | ||
}) | ||
.catch(([cardError, resultError]) => { | ||
setState(prevState => ({ | ||
...prevState, | ||
result: null, | ||
card: null, | ||
loading: false, | ||
cardError, | ||
resultError, | ||
})); | ||
}); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!isInitialized || !isLoggedIn) { | ||
setState({ | ||
loading: false, | ||
card: null, | ||
result: null, | ||
cardError: null, | ||
resultError: null, | ||
}); | ||
} else { | ||
loadCardData({ questionId }); | ||
} | ||
}, [isInitialized, isLoggedIn, questionId]); | ||
|
||
const changeVisualization = (newQuestion: Question) => { | ||
setState({ | ||
card: newQuestion.card(), | ||
result: result, | ||
loading: false, | ||
}); | ||
}; | ||
|
||
if (!isInitialized) { | ||
return null; | ||
} | ||
|
||
if (!isLoggedIn) { | ||
return ( | ||
<div> | ||
<Text>You should be logged in to see this content.</Text> | ||
</div> | ||
); | ||
} | ||
|
||
const isLoading = loading || (!result && !resultError); | ||
|
||
return ( | ||
<LoadingAndErrorWrapper | ||
className="flex-full full-width" | ||
loading={isLoading} | ||
error={cardError || resultError} | ||
noWrapper | ||
> | ||
{() => { | ||
const question = new Question(card, metadata); | ||
const legacyQuery = question.legacyQuery({ | ||
useStructuredQuery: true, | ||
}); | ||
|
||
return ( | ||
<Group h="100%" pos="relative" align="flex-start"> | ||
{showVisualizationSelector && ( | ||
<Box w="355px"> | ||
<ChartTypeSidebar | ||
question={question} | ||
result={result} | ||
onOpenChartSettings={onOpenChartSettings} | ||
onCloseChartType={onCloseChartType} | ||
query={legacyQuery} | ||
setUIControls={setUIControls} | ||
updateQuestion={changeVisualization} | ||
/> | ||
</Box> | ||
)} | ||
<QueryVisualization | ||
className="flex full-width" | ||
question={question} | ||
rawSeries={[{ card, data: result?.data }]} | ||
isRunning={isLoading} | ||
isObjectDetail={false} | ||
isResultDirty={false} | ||
isNativeEditorOpen={false} | ||
result={result} | ||
noHeader | ||
mode={PublicMode} | ||
/> | ||
</Group> | ||
); | ||
}} | ||
</LoadingAndErrorWrapper> | ||
); | ||
}; |
2 changes: 2 additions & 0 deletions
2
enterprise/frontend/src/embedding-sdk/components/public/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { QueryVisualizationSdk } from "./QueryVisualization"; | ||
export { MetabaseProvider } from "./MetabaseProvider"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const DEFAULT_FONT = "Lato"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { createContext, useContext } from "react"; | ||
|
||
interface EmbeddingSdkContextData { | ||
isInitialized: boolean; | ||
isLoggedIn: boolean; | ||
} | ||
|
||
export const EmbeddingContext = createContext<EmbeddingSdkContextData>({ | ||
isInitialized: false, | ||
isLoggedIn: false, | ||
}); | ||
|
||
export const useEmbeddingContext = () => { | ||
return useContext(EmbeddingContext); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./public"; | ||
export * from "./private"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./use-init-data"; |
55 changes: 55 additions & 0 deletions
55
enterprise/frontend/src/embedding-sdk/hooks/private/use-init-data.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { useEffect, useState } from "react"; | ||
import _ from "underscore"; | ||
|
||
import { reloadSettings } from "metabase/admin/settings/settings"; | ||
import api from "metabase/lib/api"; | ||
import { useDispatch } from "metabase/lib/redux"; | ||
import { refreshCurrentUser } from "metabase/redux/user"; | ||
import registerVisualizations from "metabase/visualizations/register"; | ||
|
||
import type { SDKConfigType } from "../../types"; | ||
|
||
const registerVisualizationsOnce = _.once(registerVisualizations); | ||
|
||
interface InitDataLoaderParameters { | ||
config: SDKConfigType; | ||
} | ||
|
||
export const useInitData = ({ | ||
config, | ||
}: InitDataLoaderParameters): { | ||
isLoggedIn: boolean; | ||
isInitialized: boolean; | ||
} => { | ||
const dispatch = useDispatch(); | ||
const [isInitialized, setIsInitialized] = useState(false); | ||
const [isLoggedIn, setIsLoggedIn] = useState(false); | ||
|
||
useEffect(() => { | ||
registerVisualizationsOnce(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
api.basename = config.metabaseInstanceUrl; | ||
|
||
if (config.authType === "apiKey" && config.apiKey) { | ||
api.apiKey = config.apiKey; | ||
} else { | ||
setIsLoggedIn(false); | ||
return; | ||
} | ||
|
||
Promise.all([ | ||
dispatch(refreshCurrentUser()), | ||
dispatch(reloadSettings()), | ||
]).then(() => { | ||
setIsInitialized(true); | ||
setIsLoggedIn(true); | ||
}); | ||
}, [config, dispatch]); | ||
|
||
return { | ||
isLoggedIn, | ||
isInitialized, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from "./use-current-user"; | ||
export * from "./use-application-name"; | ||
export * from "./use-question-search"; | ||
export * from "./use-available-fonts"; |
4 changes: 4 additions & 0 deletions
4
enterprise/frontend/src/embedding-sdk/hooks/public/use-application-name.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { useSelector } from "metabase/lib/redux"; | ||
import { getApplicationName } from "metabase/selectors/whitelabel"; | ||
|
||
export const useApplicationName = () => useSelector(getApplicationName); |
8 changes: 8 additions & 0 deletions
8
enterprise/frontend/src/embedding-sdk/hooks/public/use-available-fonts.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { useSelector } from "metabase/lib/redux"; | ||
import { getSetting } from "metabase/selectors/settings"; | ||
|
||
export const useAvailableFonts = () => { | ||
return { | ||
availableFonts: useSelector(state => getSetting(state, "available-fonts")), | ||
}; | ||
}; |
Oops, something went wrong.