Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
M
Metabase
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Iterations
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Package registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Engineering Digital Service
Metabase
Commits
7d87d7cf
Unverified
Commit
7d87d7cf
authored
5 months ago
by
Oisin Coveney
Committed by
GitHub
5 months ago
Browse files
Options
Downloads
Patches
Plain Diff
Split View into components (#48929)
parent
c712c35c
No related branches found
Branches containing commit
Tags
blah
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
frontend/src/metabase/query_builder/components/view/View.jsx
+574
-477
574 additions, 477 deletions
frontend/src/metabase/query_builder/components/view/View.jsx
with
574 additions
and
477 deletions
frontend/src/metabase/query_builder/components/view/View.jsx
+
574
−
477
View file @
7d87d7cf
/* eslint-disable react/prop-types */
import
{
Component
}
from
"
react
"
;
import
{
connect
}
from
"
react-redux
"
;
import
{
match
}
from
"
ts-pattern
"
;
import
{
t
}
from
"
ttag
"
;
...
...
@@ -60,28 +60,121 @@ const fadeIn = {
transitionProperty
:
"
opacity
"
,
};
class
View
extends
Component
{
getLeftSidebar
=
()
=>
{
const
{
question
,
result
,
isShowingChartSettingsSidebar
,
isShowingChartTypeSidebar
,
onCloseChartSettings
,
}
=
this
.
props
;
if
(
isShowingChartSettingsSidebar
)
{
const
{
question
,
result
,
addField
,
initialChartSetting
,
onReplaceAllVisualizationSettings
,
onOpenChartType
,
visualizationSettings
,
showSidebarTitle
,
}
=
this
.
props
;
return
(
const
ViewHeaderContainer
=
props
=>
{
const
{
question
,
onUnarchive
,
onMove
,
onDeletePermanently
}
=
props
;
const
query
=
question
.
query
();
const
card
=
question
.
card
();
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
query
);
const
isNewQuestion
=
!
isNative
&&
Lib
.
sourceTableOrCardId
(
query
)
===
null
;
return
(
<
QueryBuilderViewHeaderContainer
>
{
card
.
archived
&&
(
<
ArchivedEntityBanner
name
=
{
card
.
name
}
entityType
=
{
card
.
type
}
canWrite
=
{
card
.
can_write
}
canRestore
=
{
card
.
can_restore
}
canDelete
=
{
card
.
can_delete
}
onUnarchive
=
{
()
=>
onUnarchive
(
question
)
}
onMove
=
{
collection
=>
onMove
(
question
,
collection
)
}
onDeletePermanently
=
{
()
=>
onDeletePermanently
(
card
.
id
)
}
/>
)
}
<
BorderedViewTitleHeader
{
...
props
}
style
=
{
{
transition
:
"
opacity 300ms linear
"
,
opacity
:
isNewQuestion
?
0
:
1
,
}
}
/>
{
/*This is used so that the New Question Header is unmounted after the animation*/
}
<
Transition
mounted
=
{
isNewQuestion
}
transition
=
{
fadeIn
}
duration
=
{
300
}
>
{
style
=>
<
NewQuestionHeader
className
=
{
CS
.
spread
}
style
=
{
style
}
/>
}
</
Transition
>
</
QueryBuilderViewHeaderContainer
>
);
};
const
ViewMainContainer
=
props
=>
{
const
{
queryBuilderMode
,
mode
,
question
,
showLeftSidebar
,
showRightSidebar
,
parameters
,
setParameterValue
,
isLiveResizable
,
updateQuestion
,
}
=
props
;
if
(
queryBuilderMode
===
"
notebook
"
)
{
// we need to render main only in view mode
return
;
}
const
queryMode
=
mode
&&
mode
.
queryMode
();
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
const
isSidebarOpen
=
showLeftSidebar
||
showRightSidebar
;
return
(
<
QueryBuilderMain
isSidebarOpen
=
{
isSidebarOpen
}
data-testid
=
"query-builder-main"
>
{
isNative
?
(
<
ViewNativeQueryEditor
{
...
props
}
/>
)
:
(
<
StyledSyncedParametersList
parameters
=
{
parameters
}
setParameterValue
=
{
setParameterValue
}
commitImmediately
/>
)
}
<
StyledDebouncedFrame
enabled
=
{
!
isLiveResizable
}
>
<
QueryVisualization
{
...
props
}
noHeader
className
=
{
CS
.
spread
}
mode
=
{
queryMode
}
/>
</
StyledDebouncedFrame
>
<
TimeseriesChrome
question
=
{
question
}
updateQuestion
=
{
updateQuestion
}
className
=
{
CS
.
flexNoShrink
}
/>
<
ViewFooter
className
=
{
CS
.
flexNoShrink
}
/>
</
QueryBuilderMain
>
);
};
const
ViewLeftSidebarContainer
=
({
question
,
result
,
isShowingChartSettingsSidebar
,
isShowingChartTypeSidebar
,
onCloseChartSettings
,
addField
,
initialChartSetting
,
onReplaceAllVisualizationSettings
,
onOpenChartType
,
visualizationSettings
,
showSidebarTitle
,
})
=>
match
({
isShowingChartSettingsSidebar
,
isShowingChartTypeSidebar
,
})
.
with
(
{
isShowingChartSettingsSidebar
:
true
,
},
()
=>
(
<
ChartSettingsSidebar
question
=
{
question
}
result
=
{
result
}
...
...
@@ -93,493 +186,497 @@ class View extends Component {
showSidebarTitle
=
{
showSidebarTitle
}
onClose
=
{
onCloseChartSettings
}
/>
);
}
),
)
.
with
(
{
isShowingChartTypeSidebar
:
true
,
},
()
=>
<
ChartTypeSidebar
question
=
{
question
}
result
=
{
result
}
/>,
)
.
otherwise
(()
=>
null
);
const
ViewNativeQueryEditor
=
props
=>
{
const
{
question
,
height
,
isDirty
,
isNativeEditorOpen
,
card
,
setParameterValueToDefault
,
onSetDatabaseId
,
}
=
props
;
const
legacyQuery
=
question
.
legacyQuery
();
// Normally, when users open native models,
// they open an ad-hoc GUI question using the model as a data source
// (using the `/dataset` endpoint instead of the `/card/:id/query`)
// However, users without data permission open a real model as they can't use the `/dataset` endpoint
// So the model is opened as an underlying native question and the query editor becomes visible
// This check makes it hide the editor in this particular case
// More details: https://github.com/metabase/metabase/pull/20161
const
{
isEditable
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
if
(
question
.
type
()
===
"
model
"
&&
!
isEditable
)
{
return
null
;
}
if
(
isShowingChartTypeSidebar
)
{
return
<
ChartTypeSidebar
question
=
{
question
}
result
=
{
result
}
/>;
}
return
(
<
NativeQueryEditorContainer
>
<
NativeQueryEditor
{
...
props
}
query
=
{
legacyQuery
}
viewHeight
=
{
height
}
isOpen
=
{
legacyQuery
.
isEmpty
()
||
isDirty
}
isInitiallyOpen
=
{
isNativeEditorOpen
}
datasetQuery
=
{
card
&&
card
.
dataset_query
}
setParameterValueToDefault
=
{
setParameterValueToDefault
}
onSetDatabaseId
=
{
onSetDatabaseId
}
/>
</
NativeQueryEditorContainer
>
);
};
return
null
;
};
getRightSidebarForStructuredQuery
=
()
=>
{
const
{
question
,
timelines
,
isShowingSummarySidebar
,
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
updateQuestion
,
visibleTimelineEventIds
,
selectedTimelineEventIds
,
xDomain
,
showTimelineEvents
,
hideTimelineEvents
,
selectTimelineEvents
,
deselectTimelineEvents
,
onOpenModal
,
onCloseSummary
,
onCloseTimelines
,
onCloseQuestionInfo
,
onSave
,
}
=
this
.
props
;
const
isSaved
=
question
.
isSaved
();
if
(
isShowingSummarySidebar
)
{
const
query
=
question
.
query
();
return
(
const
ViewRightSidebarContainer
=
props
=>
{
const
{
question
,
deselectTimelineEvents
,
hideTimelineEvents
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
isShowingSummarySidebar
,
isShowingTimelineSidebar
,
onCloseQuestionInfo
,
onCloseSummary
,
onCloseTimelines
,
onOpenModal
,
onSave
,
selectTimelineEvents
,
selectedTimelineEventIds
,
showTimelineEvents
,
timelines
,
updateQuestion
,
visibleTimelineEventIds
,
xDomain
,
}
=
props
;
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
return
!
isNative
?
(
<
StructuredQueryRightSidebar
deselectTimelineEvents
=
{
deselectTimelineEvents
}
hideTimelineEvents
=
{
hideTimelineEvents
}
isShowingQuestionInfoSidebar
=
{
isShowingQuestionInfoSidebar
}
isShowingQuestionSettingsSidebar
=
{
isShowingQuestionSettingsSidebar
}
isShowingSummarySidebar
=
{
isShowingSummarySidebar
}
isShowingTimelineSidebar
=
{
isShowingTimelineSidebar
}
onCloseQuestionInfo
=
{
onCloseQuestionInfo
}
onCloseSummary
=
{
onCloseSummary
}
onCloseTimelines
=
{
onCloseTimelines
}
onOpenModal
=
{
onOpenModal
}
onSave
=
{
onSave
}
question
=
{
question
}
selectTimelineEvents
=
{
selectTimelineEvents
}
selectedTimelineEventIds
=
{
selectedTimelineEventIds
}
showTimelineEvents
=
{
showTimelineEvents
}
timelines
=
{
timelines
}
updateQuestion
=
{
updateQuestion
}
visibleTimelineEventIds
=
{
visibleTimelineEventIds
}
xDomain
=
{
xDomain
}
/>
)
:
(
<
NativeQueryRightSidebar
{
...
props
}
/>
);
};
const
StructuredQueryRightSidebar
=
({
deselectTimelineEvents
,
hideTimelineEvents
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
isShowingSummarySidebar
,
isShowingTimelineSidebar
,
onCloseQuestionInfo
,
onCloseSummary
,
onCloseTimelines
,
onOpenModal
,
onSave
,
question
,
selectTimelineEvents
,
selectedTimelineEventIds
,
showTimelineEvents
,
timelines
,
updateQuestion
,
visibleTimelineEventIds
,
xDomain
,
})
=>
match
({
isSaved
:
question
.
isSaved
(),
isShowingSummarySidebar
,
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
})
.
with
(
{
isShowingSummarySidebar
:
true
,
},
()
=>
(
<
SummarizeSidebar
query
=
{
que
ry
}
query
=
{
que
stion
.
query
()
}
onQueryChange
=
{
nextQuery
=>
{
const
datesetQuery
=
Lib
.
toLegacyQuery
(
nextQuery
);
const
nextQuestion
=
question
.
setDatasetQuery
(
datesetQuery
);
updateQuestion
(
nextQuestion
.
setDefaultDisplay
(),
{
run
:
true
});
updateQuestion
(
nextQuestion
.
setDefaultDisplay
(),
{
run
:
true
,
});
}
}
onClose
=
{
onCloseSummary
}
/>
)
;
}
if
(
isShowing
TimelineSidebar
)
{
return
(
<
T
imeline
Sidebar
question
=
{
question
}
timelines
=
{
timeline
s
}
visibleTimelineEventIds
=
{
visibleTimelineEventIds
}
selected
TimelineEvent
Id
s
=
{
s
elected
TimelineEvent
Id
s
}
xDomain
=
{
xDomain
}
onS
how
TimelineEvents
=
{
s
how
TimelineEvents
}
onHide
TimelineEvents
=
{
hi
deTimelineEvents
}
onSelectTimelineEvents
=
{
selectTimelineEvents
}
onDeselectTimelineEvents
=
{
deselect
Timeline
Event
s
}
onOpenModal
=
{
onOpenModal
}
onClose
=
{
onCloseTimelines
}
/>
);
}
if
(
isSaved
&&
isShowingQuestionInfoSidebar
)
{
return
(
)
,
)
.
with
({
isShowingTimelineSidebar
:
true
},
()
=>
(
<
TimelineSidebar
question
=
{
question
}
t
imeline
s
=
{
timelines
}
visibleTimelineEventIds
=
{
visibleTimelineEventIds
}
selectedTimelineEventIds
=
{
selectedTimelineEventId
s
}
xDomain
=
{
xDomain
}
onShow
TimelineEvents
=
{
s
how
TimelineEvents
}
onHideTimelineEvents
=
{
hideTimelineEvents
}
onS
elect
TimelineEvents
=
{
s
elect
TimelineEvents
}
onDeselect
TimelineEvents
=
{
de
select
TimelineEvents
}
onOpenModal
=
{
onOpenModal
}
onClose
=
{
onClose
Timelines
}
/>
))
.
with
(
{
isSaved
:
true
,
isShowingQuestionInfoSidebar
:
true
,
},
()
=>
(
<
QuestionInfoSidebar
question
=
{
question
}
onSave
=
{
onSave
}
onClose
=
{
onCloseQuestionInfo
}
/>
);
}
if
(
isSaved
&&
isShowingQuestionSettingsSidebar
)
{
return
<
QuestionSettingsSidebar
question
=
{
question
}
/>;
}
return
null
;
};
getRightSidebarForNativeQuery
=
()
=>
{
const
{
isShowingTemplateTagsEditor
,
isShowingDataReference
,
isShowingSnippetSidebar
,
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
toggleTemplateTagsEditor
,
toggleDataReference
,
toggleSnippetSidebar
,
showTimelineEvent
,
showTimelineEvents
,
hideTimelineEvents
,
selectTimelineEvents
,
deselectTimelineEvents
,
onCloseTimelines
,
onCloseQuestionInfo
,
onSave
,
question
,
}
=
this
.
props
;
if
(
isShowingTemplateTagsEditor
)
{
return
(
<
TagEditorSidebar
{
...
this
.
props
}
query
=
{
question
.
legacyQuery
()
}
onClose
=
{
toggleTemplateTagsEditor
}
/>
);
}
if
(
isShowingDataReference
)
{
return
<
DataReference
{
...
this
.
props
}
onClose
=
{
toggleDataReference
}
/>;
}
if
(
isShowingSnippetSidebar
)
{
return
<
SnippetSidebar
{
...
this
.
props
}
onClose
=
{
toggleSnippetSidebar
}
/>;
}
if
(
isShowingTimelineSidebar
)
{
return
(
<
TimelineSidebar
{
...
this
.
props
}
onShowTimelineEvent
=
{
showTimelineEvent
}
onShowTimelineEvents
=
{
showTimelineEvents
}
onHideTimelineEvents
=
{
hideTimelineEvents
}
onSelectTimelineEvents
=
{
selectTimelineEvents
}
onDeselectTimelineEvents
=
{
deselectTimelineEvents
}
onClose
=
{
onCloseTimelines
}
/>
);
}
if
(
isShowingQuestionInfoSidebar
)
{
return
(
<
QuestionInfoSidebar
question
=
{
question
}
onSave
=
{
onSave
}
onClose
=
{
onCloseQuestionInfo
}
/>
);
}
if
(
isShowingQuestionSettingsSidebar
)
{
return
<
QuestionSettingsSidebar
question
=
{
question
}
/>;
}
return
null
;
};
getRightSidebar
=
()
=>
{
const
{
question
}
=
this
.
props
;
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
),
)
.
with
(
{
isSaved
:
true
,
isShowingQuestionSettingsSidebar
:
true
,
},
()
=>
<
QuestionSettingsSidebar
question
=
{
question
}
/>,
)
.
otherwise
(()
=>
null
);
const
NativeQueryRightSidebar
=
props
=>
{
const
{
question
,
toggleTemplateTagsEditor
,
toggleDataReference
,
toggleSnippetSidebar
,
showTimelineEvent
,
showTimelineEvents
,
hideTimelineEvents
,
selectTimelineEvents
,
deselectTimelineEvents
,
onCloseTimelines
,
onSave
,
onCloseQuestionInfo
,
isShowingTemplateTagsEditor
,
isShowingDataReference
,
isShowingSnippetSidebar
,
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
}
=
props
;
return
match
({
isShowingTemplateTagsEditor
,
isShowingDataReference
,
isShowingSnippetSidebar
,
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
})
.
with
({
isShowingTemplateTagsEditor
:
true
},
()
=>
(
<
TagEditorSidebar
{
...
props
}
query
=
{
question
.
legacyQuery
()
}
onClose
=
{
toggleTemplateTagsEditor
}
/>
))
.
with
({
isShowingDataReference
:
true
},
()
=>
(
<
DataReference
{
...
props
}
onClose
=
{
toggleDataReference
}
/>
))
.
with
({
isShowingSnippetSidebar
:
true
},
()
=>
(
<
SnippetSidebar
{
...
props
}
onClose
=
{
toggleSnippetSidebar
}
/>
))
.
with
({
isShowingTimelineSidebar
:
true
},
()
=>
(
<
TimelineSidebar
{
...
props
}
onShowTimelineEvent
=
{
showTimelineEvent
}
onShowTimelineEvents
=
{
showTimelineEvents
}
onHideTimelineEvents
=
{
hideTimelineEvents
}
onSelectTimelineEvents
=
{
selectTimelineEvents
}
onDeselectTimelineEvents
=
{
deselectTimelineEvents
}
onClose
=
{
onCloseTimelines
}
/>
))
.
with
({
isShowingQuestionInfoSidebar
:
true
},
()
=>
(
<
QuestionInfoSidebar
question
=
{
question
}
onSave
=
{
onSave
}
onClose
=
{
onCloseQuestionInfo
}
/>
))
.
with
({
isShowingQuestionSettingsSidebar
:
true
},
()
=>
(
<
QuestionSettingsSidebar
question
=
{
question
}
/>
))
.
otherwise
(()
=>
null
);
};
return
!
isNative
?
this
.
getRightSidebarForStructuredQuery
()
:
this
.
getRightSidebarForNativeQuery
();
};
const
View
=
props
=>
{
const
{
question
,
result
,
rawSeries
,
databases
,
isShowingNewbModal
,
isShowingTimelineSidebar
,
queryBuilderMode
,
closeQbNewbModal
,
onDismissToast
,
onConfirmToast
,
isShowingToaster
,
isHeaderVisible
,
updateQuestion
,
reportTimezone
,
readOnly
,
isDirty
,
isRunning
,
isRunnable
,
isResultDirty
,
hasVisualizeButton
,
runQuestionQuery
,
cancelQuery
,
setQueryBuilderMode
,
runDirtyQuestionQuery
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
cancelQuestionChanges
,
onCreate
,
onSave
,
onChangeLocation
,
questionAlerts
,
user
,
modal
,
modalContext
,
card
,
onCloseModal
,
onOpenModal
,
originalQuestion
,
isShowingChartSettingsSidebar
,
isShowingChartTypeSidebar
,
onCloseChartSettings
,
addField
,
initialChartSetting
,
onReplaceAllVisualizationSettings
,
onOpenChartType
,
visualizationSettings
,
showSidebarTitle
,
isShowingSummarySidebar
,
isShowingTemplateTagsEditor
,
isShowingDataReference
,
isShowingSnippetSidebar
,
}
=
props
;
// if we don't have a question at all or no databases then we are initializing, so keep it simple
if
(
!
question
||
!
databases
)
{
return
<
LoadingAndErrorWrapper
className
=
{
CS
.
fullHeight
}
loading
/>;
}
renderHeader
=
()
=>
{
const
{
question
,
onUnarchive
,
onMove
,
onDeletePermanently
}
=
this
.
props
;
const
query
=
question
.
query
();
const
card
=
question
.
card
();
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
query
);
const
query
=
question
.
query
();
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
const
isNewQuestion
=
!
isNative
&&
Lib
.
sourceTableOrCardId
(
query
)
===
null
;
const
isNewQuestion
=
!
isNative
&&
Lib
.
sourceTableOrCardId
(
query
)
===
null
;
const
isModel
=
question
.
type
()
===
"
model
"
;
const
isMetric
=
question
.
type
()
===
"
metric
"
;
if
((
isModel
||
isMetric
)
&&
queryBuilderMode
===
"
dataset
"
)
{
return
(
<
QueryBuilderViewHeaderContainer
>
{
card
.
archived
&&
(
<
ArchivedEntityBanner
name
=
{
card
.
name
}
entityType
=
{
card
.
type
}
canWrite
=
{
card
.
can_write
}
canRestore
=
{
card
.
can_restore
}
canDelete
=
{
card
.
can_delete
}
onUnarchive
=
{
()
=>
onUnarchive
(
question
)
}
onMove
=
{
collection
=>
onMove
(
question
,
collection
)
}
onDeletePermanently
=
{
()
=>
onDeletePermanently
(
card
.
id
)
}
<>
{
isModel
&&
<
DatasetEditor
{
...
props
}
/>
}
{
isMetric
&&
(
<
MetricEditor
question
=
{
question
}
result
=
{
result
}
rawSeries
=
{
rawSeries
}
reportTimezone
=
{
reportTimezone
}
isDirty
=
{
isDirty
}
isResultDirty
=
{
isResultDirty
}
isRunning
=
{
isRunning
}
onChange
=
{
updateQuestion
}
onCreate
=
{
async
question
=>
{
await
onCreate
(
question
);
setQueryBuilderMode
(
"
view
"
);
}
}
onSave
=
{
async
question
=>
{
await
onSave
(
question
);
setQueryBuilderMode
(
"
view
"
);
}
}
onCancel
=
{
question
=>
{
if
(
question
.
isSaved
())
{
cancelQuestionChanges
();
runDirtyQuestionQuery
();
setQueryBuilderMode
(
"
view
"
);
}
else
{
onChangeLocation
(
"
/
"
);
}
}
}
onRunQuery
=
{
runQuestionQuery
}
onCancelQuery
=
{
cancelQuery
}
/>
)
}
<
BorderedViewTitleHeader
{
...
this
.
props
}
style
=
{
{
transition
:
"
opacity 300ms linear
"
,
opacity
:
isNewQuestion
?
0
:
1
,
}
}
/>
{
/*This is used so that the New Question Header is unmounted after the animation*/
}
<
Transition
mounted
=
{
isNewQuestion
}
transition
=
{
fadeIn
}
duration
=
{
300
}
>
{
style
=>
<
NewQuestionHeader
className
=
{
CS
.
spread
}
style
=
{
style
}
/>
}
</
Transition
>
</
QueryBuilderViewHeaderContainer
>
);
};
renderNativeQueryEditor
=
()
=>
{
const
{
question
,
card
,
height
,
isDirty
,
isNativeEditorOpen
,
setParameterValueToDefault
,
onSetDatabaseId
,
}
=
this
.
props
;
const
legacyQuery
=
question
.
legacyQuery
();
// Normally, when users open native models,
// they open an ad-hoc GUI question using the model as a data source
// (using the `/dataset` endpoint instead of the `/card/:id/query`)
// However, users without data permission open a real model as they can't use the `/dataset` endpoint
// So the model is opened as an underlying native question and the query editor becomes visible
// This check makes it hide the editor in this particular case
// More details: https://github.com/metabase/metabase/pull/20161
const
{
isEditable
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
if
(
question
.
type
()
===
"
model
"
&&
!
isEditable
)
{
return
null
;
}
return
(
<
NativeQueryEditorContainer
>
<
NativeQueryEditor
{
...
this
.
props
}
query
=
{
legacyQuery
}
viewHeight
=
{
height
}
isOpen
=
{
legacyQuery
.
isEmpty
()
||
isDirty
}
isInitiallyOpen
=
{
isNativeEditorOpen
}
datasetQuery
=
{
card
&&
card
.
dataset_query
}
setParameterValueToDefault
=
{
setParameterValueToDefault
}
onSetDatabaseId
=
{
onSetDatabaseId
}
<
QueryModals
questionAlerts
=
{
questionAlerts
}
user
=
{
user
}
onSave
=
{
onSave
}
onCreate
=
{
onCreate
}
updateQuestion
=
{
updateQuestion
}
modal
=
{
modal
}
modalContext
=
{
modalContext
}
card
=
{
card
}
question
=
{
question
}
onCloseModal
=
{
onCloseModal
}
onOpenModal
=
{
onOpenModal
}
setQueryBuilderMode
=
{
setQueryBuilderMode
}
originalQuestion
=
{
originalQuestion
}
onChangeLocation
=
{
onChangeLocation
}
/>
</
NativeQueryEditorContainer
>
</>
);
};
renderMain
=
({
leftSidebar
,
rightSidebar
})
=>
{
const
{
question
,
mode
,
parameters
,
isLiveResizable
,
setParameterValue
,
queryBuilderMode
,
}
=
this
.
props
;
if
(
queryBuilderMode
===
"
notebook
"
)
{
// we need to render main only in view mode
return
;
}
const
queryMode
=
mode
&&
mode
.
queryMode
();
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
const
isSidebarOpen
=
leftSidebar
||
rightSidebar
;
}
return
(
<
QueryBuilderMain
isSidebarOpen
=
{
isSidebarOpen
}
data-testid
=
"query-builder-main"
const
isNotebookContainerOpen
=
isNewQuestion
||
queryBuilderMode
===
"
notebook
"
;
const
showLeftSidebar
=
isShowingChartSettingsSidebar
||
isShowingChartTypeSidebar
;
const
showRightSidebar
=
isShowingTimelineSidebar
||
isShowingQuestionInfoSidebar
||
isShowingQuestionSettingsSidebar
||
(
!
isNative
&&
isShowingSummarySidebar
)
||
(
isNative
&&
(
isShowingTemplateTagsEditor
||
isShowingDataReference
||
isShowingSnippetSidebar
));
const
rightSidebarWidth
=
match
({
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
})
.
with
({
isShowingTimelineSidebar
:
true
},
()
=>
SIDEBAR_SIZES
.
TIMELINE
)
.
with
({
isShowingQuestionInfoSidebar
:
true
},
()
=>
0
)
.
with
({
isShowingQuestionSettingsSidebar
:
true
},
()
=>
0
)
.
otherwise
(()
=>
SIDEBAR_SIZES
.
NORMAL
);
return
(
<
div
className
=
{
CS
.
fullHeight
}
>
<
QueryBuilderViewRoot
className
=
{
QueryBuilderS
.
QueryBuilder
}
data-testid
=
"query-builder-root"
>
{
isNative
?
(
this
.
renderNativeQueryEditor
()
)
:
(
<
StyledSyncedParametersList
parameters
=
{
parameters
}
setParameterValue
=
{
setParameterValue
}
commitImmediately
/>
)
}
{
isHeaderVisible
&&
<
ViewHeaderContainer
{
...
props
}
/>
}
<
StyledDebouncedFrame
enabled
=
{
!
isLiveResizable
}
>
<
QueryVisualization
{
...
this
.
props
}
noHeader
className
=
{
CS
.
spread
}
mode
=
{
queryMode
}
/>
</
StyledDebouncedFrame
>
<
TimeseriesChrome
question
=
{
this
.
props
.
question
}
updateQuestion
=
{
this
.
props
.
updateQuestion
}
className
=
{
CS
.
flexNoShrink
}
/>
<
ViewFooter
className
=
{
CS
.
flexNoShrink
}
/>
</
QueryBuilderMain
>
);
};
render
()
{
const
{
question
,
result
,
rawSeries
,
databases
,
isShowingNewbModal
,
isShowingTimelineSidebar
,
queryBuilderMode
,
closeQbNewbModal
,
onDismissToast
,
onConfirmToast
,
isShowingToaster
,
isHeaderVisible
,
updateQuestion
,
reportTimezone
,
readOnly
,
isDirty
,
isRunning
,
isRunnable
,
isResultDirty
,
hasVisualizeButton
,
runQuestionQuery
,
cancelQuery
,
setQueryBuilderMode
,
runDirtyQuestionQuery
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
cancelQuestionChanges
,
onCreate
,
onSave
,
onChangeLocation
,
}
=
this
.
props
;
// if we don't have a question at all or no databases then we are initializing, so keep it simple
if
(
!
question
||
!
databases
)
{
return
<
LoadingAndErrorWrapper
className
=
{
CS
.
fullHeight
}
loading
/>;
}
const
query
=
question
.
query
();
const
{
isNative
}
=
Lib
.
queryDisplayInfo
(
question
.
query
());
const
isNewQuestion
=
!
isNative
&&
Lib
.
sourceTableOrCardId
(
query
)
===
null
;
const
isModel
=
question
.
type
()
===
"
model
"
;
const
isMetric
=
question
.
type
()
===
"
metric
"
;
if
((
isModel
||
isMetric
)
&&
queryBuilderMode
===
"
dataset
"
)
{
return
(
<>
{
isModel
&&
<
DatasetEditor
{
...
this
.
props
}
/>
}
{
isMetric
&&
(
<
MetricEditor
question
=
{
question
}
result
=
{
result
}
rawSeries
=
{
rawSeries
}
<
QueryBuilderContentContainer
>
{
!
isNative
&&
(
<
NotebookContainer
isOpen
=
{
isNotebookContainerOpen
}
updateQuestion
=
{
updateQuestion
}
reportTimezone
=
{
reportTimezone
}
readOnly
=
{
readOnly
}
question
=
{
question
}
isDirty
=
{
isDirty
}
isRunnable
=
{
isRunnable
}
isResultDirty
=
{
isResultDirty
}
isRunning
=
{
isRunning
}
onChange
=
{
updateQuestion
}
onCreate
=
{
async
question
=>
{
await
onCreate
(
question
);
setQueryBuilderMode
(
"
view
"
);
}
}
onSave
=
{
async
question
=>
{
await
onSave
(
question
);
setQueryBuilderMode
(
"
view
"
);
}
}
onCancel
=
{
question
=>
{
if
(
question
.
isSaved
())
{
cancelQuestionChanges
();
runDirtyQuestionQuery
();
setQueryBuilderMode
(
"
view
"
);
}
else
{
onChangeLocation
(
"
/
"
);
}
}
}
onRunQuery
=
{
runQuestionQuery
}
onCancelQuery
=
{
cancelQuery
}
hasVisualizeButton
=
{
hasVisualizeButton
}
runQuestionQuery
=
{
runQuestionQuery
}
setQueryBuilderMode
=
{
setQueryBuilderMode
}
/>
)
}
<
QueryModals
questionAlerts
=
{
this
.
props
.
questionAlerts
}
user
=
{
this
.
props
.
user
}
onSave
=
{
this
.
props
.
onSave
}
onCreate
=
{
this
.
props
.
onCreate
}
updateQuestion
=
{
this
.
props
.
updateQuestion
}
modal
=
{
this
.
props
.
modal
}
modalContext
=
{
this
.
props
.
modalContext
}
card
=
{
this
.
props
.
card
}
question
=
{
this
.
props
.
question
}
onCloseModal
=
{
this
.
props
.
onCloseModal
}
onOpenModal
=
{
this
.
props
.
onOpenModal
}
setQueryBuilderMode
=
{
this
.
props
.
setQueryBuilderMode
}
originalQuestion
=
{
this
.
props
.
originalQuestion
}
onChangeLocation
=
{
this
.
props
.
onChangeLocation
}
/>
</>
);
}
const
isNotebookContainerOpen
=
isNewQuestion
||
queryBuilderMode
===
"
notebook
"
;
const
leftSidebar
=
this
.
getLeftSidebar
();
const
rightSidebar
=
this
.
getRightSidebar
();
const
rightSidebarWidth
=
match
({
isShowingTimelineSidebar
,
isShowingQuestionInfoSidebar
,
isShowingQuestionSettingsSidebar
,
})
.
with
({
isShowingTimelineSidebar
:
true
},
()
=>
SIDEBAR_SIZES
.
TIMELINE
)
.
with
({
isShowingQuestionInfoSidebar
:
true
},
()
=>
0
)
.
with
({
isShowingQuestionSettingsSidebar
:
true
},
()
=>
0
)
.
otherwise
(()
=>
SIDEBAR_SIZES
.
NORMAL
);
return
(
<
div
className
=
{
CS
.
fullHeight
}
>
<
QueryBuilderViewRoot
className
=
{
QueryBuilderS
.
QueryBuilder
}
data-testid
=
"query-builder-root"
>
{
isHeaderVisible
&&
this
.
renderHeader
()
}
<
QueryBuilderContentContainer
>
{
!
isNative
&&
(
<
NotebookContainer
isOpen
=
{
isNotebookContainerOpen
}
updateQuestion
=
{
updateQuestion
}
reportTimezone
=
{
reportTimezone
}
readOnly
=
{
readOnly
}
question
=
{
question
}
isDirty
=
{
isDirty
}
isRunnable
=
{
isRunnable
}
isResultDirty
=
{
isResultDirty
}
hasVisualizeButton
=
{
hasVisualizeButton
}
runQuestionQuery
=
{
runQuestionQuery
}
setQueryBuilderMode
=
{
setQueryBuilderMode
}
/>
)
}
<
ViewSidebar
side
=
"left"
isOpen
=
{
!!
leftSidebar
}
>
{
leftSidebar
}
</
ViewSidebar
>
{
this
.
renderMain
({
leftSidebar
,
rightSidebar
})
}
<
ViewSidebar
side
=
"right"
isOpen
=
{
!!
rightSidebar
}
width
=
{
rightSidebarWidth
}
>
{
rightSidebar
}
</
ViewSidebar
>
</
QueryBuilderContentContainer
>
</
QueryBuilderViewRoot
>
{
isShowingNewbModal
&&
(
<
SavedQuestionIntroModal
question
=
{
question
}
isShowingNewbModal
=
{
isShowingNewbModal
}
onClose
=
{
()
=>
closeQbNewbModal
()
}
<
ViewSidebar
side
=
"left"
isOpen
=
{
showLeftSidebar
}
>
<
ViewLeftSidebarContainer
question
=
{
question
}
result
=
{
result
}
isShowingChartSettingsSidebar
=
{
isShowingChartSettingsSidebar
}
isShowingChartTypeSidebar
=
{
isShowingChartTypeSidebar
}
onCloseChartSettings
=
{
onCloseChartSettings
}
addField
=
{
addField
}
initialChartSetting
=
{
initialChartSetting
}
onReplaceAllVisualizationSettings
=
{
onReplaceAllVisualizationSettings
}
onOpenChartType
=
{
onOpenChartType
}
visualizationSettings
=
{
visualizationSettings
}
showSidebarTitle
=
{
showSidebarTitle
}
/>
</
ViewSidebar
>
<
ViewMainContainer
showLeftSidebar
=
{
showLeftSidebar
}
showRightSidebar
=
{
showRightSidebar
}
{
...
props
}
/>
)
}
<
QueryModals
questionAlerts
=
{
this
.
props
.
questionAlerts
}
user
=
{
this
.
props
.
user
}
onSave
=
{
this
.
props
.
onSave
}
onCreate
=
{
this
.
props
.
onCreate
}
updateQuestion
=
{
this
.
props
.
updateQuestion
}
modal
=
{
this
.
props
.
modal
}
modalContext
=
{
this
.
props
.
modalContext
}
card
=
{
this
.
props
.
card
}
question
=
{
this
.
props
.
question
}
onCloseModal
=
{
this
.
props
.
onCloseModal
}
onOpenModal
=
{
this
.
props
.
onOpenModal
}
setQueryBuilderMode
=
{
this
.
props
.
setQueryBuilderMode
}
originalQuestion
=
{
this
.
props
.
originalQuestion
}
onChangeLocation
=
{
this
.
props
.
onChangeLocation
}
/>
<
Toaster
message
=
{
t
`Would you like to be notified when this question is done loading?`
}
isShown
=
{
isShowingToaster
}
onDismiss
=
{
onDismissToast
}
onConfirm
=
{
onConfirmToast
}
fixed
<
ViewSidebar
side
=
"right"
isOpen
=
{
showRightSidebar
}
width
=
{
rightSidebarWidth
}
>
<
ViewRightSidebarContainer
{
...
props
}
/>
</
ViewSidebar
>
</
QueryBuilderContentContainer
>
</
QueryBuilderViewRoot
>
{
isShowingNewbModal
&&
(
<
SavedQuestionIntroModal
question
=
{
question
}
isShowingNewbModal
=
{
isShowingNewbModal
}
onClose
=
{
()
=>
closeQbNewbModal
()
}
/>
</
div
>
);
}
}
)
}
<
QueryModals
questionAlerts
=
{
questionAlerts
}
user
=
{
user
}
onSave
=
{
onSave
}
onCreate
=
{
onCreate
}
updateQuestion
=
{
updateQuestion
}
modal
=
{
modal
}
modalContext
=
{
modalContext
}
card
=
{
card
}
question
=
{
question
}
onCloseModal
=
{
onCloseModal
}
onOpenModal
=
{
onOpenModal
}
setQueryBuilderMode
=
{
setQueryBuilderMode
}
originalQuestion
=
{
originalQuestion
}
onChangeLocation
=
{
onChangeLocation
}
/>
<
Toaster
message
=
{
t
`Would you like to be notified when this question is done loading?`
}
isShown
=
{
isShowingToaster
}
onDismiss
=
{
onDismissToast
}
onConfirm
=
{
onConfirmToast
}
fixed
/>
</
div
>
);
};
const
mapDispatchToProps
=
dispatch
=>
({
onSetDatabaseId
:
id
=>
dispatch
(
rememberLastUsedDatabase
(
id
)),
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment