Skip to content
Snippets Groups Projects
Unverified Commit 132bb1a2 authored by github-automation-metabase's avatar github-automation-metabase Committed by GitHub
Browse files

feat(sdk): add withChartTypeSelector prop to InteractiveQuestion (#50664) (#50705)


* add withVisualizationSelector prop

* add tests

* add to docs

* use default layout in tests

* only use custom layout when needed

* make withChartTypeSelector false by default

Co-authored-by: default avatarPhoomparin Mano <poom@metabase.com>
parent 5fcad73c
No related branches found
No related tags found
No related merge requests found
......@@ -68,19 +68,20 @@ export default function App() {
## Question props
| Prop | Type | Description |
| ------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| questionId | number or string | (required) The ID of the question. This is either:<br>- The numerical ID when accessing a question link, e.g., `http://localhost:3000/question/1-my-question` where the ID is `1`.<br>- The `entity_id` key of the question object. You can find a question's entity ID in the info panel when viewing a question. |
| plugins | `{ mapQuestionClickActions: Function }` or null | Additional mapper function to override or add drill-down menu. |
| height | number or string | (optional) A number or string specifying a CSS size value that specifies the height of the component |
| entityTypeFilter | string array; options include "table", "question", "model", "metric" | (optional) An array that specifies which entity types are available in the data picker |
| isSaveEnabled | boolean | (optional) Whether people can save the question. |
| withResetButton | boolean | (optional, default: `true`) Determines whether a reset button is displayed. Only relevant when using the default layout |
| withTitle | boolean | (optional, default: `false`) Determines whether the question title is displayed. Only relevant when using the default layout. |
| customTitle | string or undefined | (optional) Allows a custom title to be displayed instead of the default question title. Only relevant when using the default layout. |
| onBeforeSave | `() => void` | (optional) A callback function that triggers before saving. Only relevant when `isSaveEnabled = true`. |
| onSave | `() => void` | (optional) A callback function that triggers when a user saves the question. Only relevant when `isSaveEnabled = true`. |
| saveToCollectionId | number | (optional) The target collection to save the question to. This will hide the collection picker from the save modal. Only applicable to static questions. |
| Prop | Type | Description |
|-----------------------|----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| questionId | number or string | (required) The ID of the question. This is either:<br>- The numerical ID when accessing a question link, e.g., `http://localhost:3000/question/1-my-question` where the ID is `1`.<br>- The `entity_id` key of the question object. You can find a question's entity ID in the info panel when viewing a question. |
| plugins | `{ mapQuestionClickActions: Function }` or null | Additional mapper function to override or add drill-down menu. |
| height | number or string | (optional) A number or string specifying a CSS size value that specifies the height of the component |
| entityTypeFilter | string array; options include "table", "question", "model", "metric" | (optional) An array that specifies which entity types are available in the data picker |
| isSaveEnabled | boolean | (optional) Whether people can save the question. |
| withResetButton | boolean | (optional, default: `true`) Determines whether a reset button is displayed. Only relevant when using the default layout |
| withTitle | boolean | (optional, default: `false`) Determines whether the question title is displayed. Only relevant when using the default layout. |
| customTitle | string or undefined | (optional) Allows a custom title to be displayed instead of the default question title. Only relevant when using the default layout. |
| withChartTypeSelector | boolean | (optional, default: `true`) Determines whether the chart type selector is shown. Only relevant when using the default layout. |
| onBeforeSave | `() => void` | (optional) A callback function that triggers before saving. Only relevant when `isSaveEnabled = true`. |
| onSave | `() => void` | (optional) A callback function that triggers when a user saves the question. Only relevant when `isSaveEnabled = true`. |
| saveToCollectionId | number | (optional) The target collection to save the question to. This will hide the collection picker from the save modal. Only applicable to static questions. |
## Customizing interactive questions
......
......@@ -39,7 +39,11 @@ export const InteractiveAdHocQuestion = ({
onNavigateBack={onNavigateBack}
>
{children ?? (
<InteractiveQuestionResult height={height} withTitle={withTitle} />
<InteractiveQuestionResult
height={height}
withTitle={withTitle}
withChartTypeSelector
/>
)}
</InteractiveQuestionProviderWithLocation>
);
......
......@@ -24,6 +24,7 @@ export interface InteractiveQuestionResultProps {
withResetButton?: boolean;
withTitle?: boolean;
customTitle?: ReactNode;
withChartTypeSelector?: boolean;
}
type QuestionView = "editor" | "filter" | "summarize" | "visualization";
......@@ -57,6 +58,7 @@ export const InteractiveQuestionResult = ({
withTitle,
customTitle,
withResetButton,
withChartTypeSelector,
}: InteractiveQuestionResultProps & FlexibleSizeProps): ReactElement => {
const [questionView, setQuestionView] =
useState<QuestionView>("visualization");
......@@ -129,7 +131,7 @@ export const InteractiveQuestionResult = ({
</Group>
<Group className={InteractiveQuestionS.MidBar} py={0} px="md">
{questionView === "visualization" && (
{withChartTypeSelector && questionView === "visualization" && (
<Button
compact
radius="xl"
......@@ -138,6 +140,7 @@ export const InteractiveQuestionResult = ({
variant="filled"
color="brand"
onClick={toggleChartTypeSelector}
data-testid="chart-type-selector-button"
>
<Group>
<Icon
......@@ -157,7 +160,9 @@ export const InteractiveQuestionResult = ({
</Group>
<Box className={InteractiveQuestionS.Main} p="md" w="100%" h="100%">
<Box className={InteractiveQuestionS.ChartTypeSelector}>
{isChartSelectorOpen && questionView === "visualization" ? (
{isChartSelectorOpen &&
withChartTypeSelector &&
questionView === "visualization" ? (
<InteractiveQuestion.ChartTypeSelector />
) : null}
</Box>
......
......@@ -59,6 +59,7 @@ export const _InteractiveQuestion = ({
entityTypeFilter,
isSaveEnabled,
saveToCollectionId,
withChartTypeSelector = true,
}: InteractiveQuestionProps &
InteractiveQuestionResultProps &
FlexibleSizeProps): JSX.Element | null => (
......@@ -80,6 +81,7 @@ export const _InteractiveQuestion = ({
customTitle={customTitle}
withResetButton={withResetButton}
withTitle={withTitle}
withChartTypeSelector={withChartTypeSelector}
/>
)}
</InteractiveQuestionProvider>
......
......@@ -53,7 +53,7 @@ const TEST_DATASET = createMockDataset({
});
// Provides a button to re-run the query
function InteractiveQuestionTestResult() {
function InteractiveQuestionCustomLayout() {
const { resetQuestion } = useInteractiveQuestionContext();
return (
......@@ -66,8 +66,12 @@ function InteractiveQuestionTestResult() {
const setup = ({
isValidCard = true,
withCustomLayout = false,
withChartTypeSelector = false,
}: {
isValidCard?: boolean;
withCustomLayout?: boolean;
withChartTypeSelector?: boolean;
} = {}) => {
const { state } = setupSdkState({
currentUser: TEST_USER,
......@@ -93,8 +97,11 @@ const setup = ({
setupCardQueryEndpoints(TEST_CARD, TEST_DATASET);
return renderWithProviders(
<InteractiveQuestion questionId={TEST_CARD.id}>
<InteractiveQuestionTestResult />
<InteractiveQuestion
questionId={TEST_CARD.id}
withChartTypeSelector={withChartTypeSelector}
>
{withCustomLayout ? <InteractiveQuestionCustomLayout /> : undefined}
</InteractiveQuestion>,
{
mode: "sdk",
......@@ -116,7 +123,7 @@ describe("InteractiveQuestion", () => {
});
it("should render loading state when rerunning the query", async () => {
setup();
setup({ withCustomLayout: true });
await waitForLoaderToBeRemoved();
......@@ -172,4 +179,22 @@ describe("InteractiveQuestion", () => {
expect(screen.getByText("Question not found")).toBeInTheDocument();
});
it("should show a chart type selector button if withChartTypeSelector is true", async () => {
setup({ withChartTypeSelector: true });
await waitForLoaderToBeRemoved();
expect(
screen.getByTestId("chart-type-selector-button"),
).toBeInTheDocument();
});
it("should not show a chart type selector button if withChartTypeSelector is false", async () => {
setup({ withChartTypeSelector: false });
await waitForLoaderToBeRemoved();
expect(
screen.queryByTestId("chart-type-selector-button"),
).not.toBeInTheDocument();
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment