Skip to content

Commit a7aa8f7

Browse files
feat(Dataset): editor improvements - run in sqllab (#33443)
1 parent ff34e3c commit a7aa8f7

File tree

10 files changed

+287
-50
lines changed

10 files changed

+287
-50
lines changed

superset-frontend/src/SqlLab/components/SqlEditor/index.tsx

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ import {
115115
LOG_ACTIONS_SQLLAB_STOP_QUERY,
116116
Logger,
117117
} from 'src/logger/LogUtils';
118+
import CopyToClipboard from 'src/components/CopyToClipboard';
118119
import TemplateParamsEditor from '../TemplateParamsEditor';
119120
import SouthPane from '../SouthPane';
120121
import SaveQuery, { QueryPayload } from '../SaveQuery';
@@ -315,6 +316,7 @@ const SqlEditor: FC<Props> = ({
315316
);
316317
const [showCreateAsModal, setShowCreateAsModal] = useState(false);
317318
const [createAs, setCreateAs] = useState('');
319+
const currentSQL = useRef<string>(queryEditor.sql);
318320
const showEmptyState = useMemo(
319321
() => !database || isEmpty(database),
320322
[database],
@@ -648,6 +650,7 @@ const SqlEditor: FC<Props> = ({
648650
);
649651

650652
const onSqlChanged = useEffectEvent((sql: string) => {
653+
currentSQL.current = sql;
651654
dispatch(queryEditorSetSql(queryEditor, sql));
652655
});
653656

@@ -890,6 +893,73 @@ const SqlEditor: FC<Props> = ({
890893
dispatch(queryEditorSetCursorPosition(queryEditor, newPosition));
891894
};
892895

896+
const copyQuery = (callback: (text: string) => void) => {
897+
callback(currentSQL.current);
898+
};
899+
const renderCopyQueryButton = () => (
900+
<Button type="primary">{t('COPY QUERY')}</Button>
901+
);
902+
903+
const renderDatasetWarning = () => (
904+
<Alert
905+
css={css`
906+
margin-bottom: ${theme.gridUnit * 2}px;
907+
padding-top: ${theme.gridUnit * 4}px;
908+
.antd5-alert-action {
909+
align-self: center;
910+
}
911+
`}
912+
type="info"
913+
action={
914+
<CopyToClipboard
915+
wrapText={false}
916+
copyNode={renderCopyQueryButton()}
917+
getText={copyQuery}
918+
/>
919+
}
920+
description={
921+
<div
922+
css={css`
923+
display: flex;
924+
justify-content: space-between;
925+
align-items: center;
926+
`}
927+
>
928+
<div
929+
css={css`
930+
display: flex;
931+
flex-direction: column;
932+
`}
933+
>
934+
<p
935+
css={css`
936+
font-size: ${theme.typography.sizes.m}px;
937+
font-weight: ${theme.typography.weights.medium};
938+
color: ${theme.colors.primary.dark2};
939+
`}
940+
>
941+
{' '}
942+
{t(`You are edting a query from the virtual dataset `) +
943+
queryEditor.name}
944+
</p>
945+
<p
946+
css={css`
947+
font-size: ${theme.typography.sizes.m}px;
948+
font-weight: ${theme.typography.weights.normal};
949+
color: ${theme.colors.primary.dark2};
950+
`}
951+
>
952+
{t(
953+
'After making the changes, copy the query and paste in the virtual dataset SQL snippet settings.',
954+
)}{' '}
955+
</p>
956+
</div>
957+
</div>
958+
}
959+
message=""
960+
/>
961+
);
962+
893963
const queryPane = () => {
894964
const { aceEditorHeight, southPaneHeight } =
895965
getAceEditorAndSouthPaneHeights(height, northPercent, southPercent);
@@ -899,7 +969,7 @@ const SqlEditor: FC<Props> = ({
899969
className="queryPane"
900970
sizes={[northPercent, southPercent]}
901971
elementStyle={elementStyle}
902-
minSize={200}
972+
minSize={queryEditor.isDataset ? 400 : 200}
903973
direction="vertical"
904974
gutterSize={SQL_EDITOR_GUTTER_HEIGHT}
905975
onDragStart={onResizeStart}
@@ -915,6 +985,7 @@ const SqlEditor: FC<Props> = ({
915985
startQuery={startQuery}
916986
/>
917987
)}
988+
{queryEditor.isDataset && renderDatasetWarning()}
918989
{isActive && (
919990
<AceEditorWrapper
920991
autocomplete={autocompleteEnabled && !isTempId(queryEditor.id)}

superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class TabbedSqlEditors extends PureComponent<TabbedSqlEditorsProps> {
141141
schema,
142142
autorun,
143143
sql,
144+
isDataset: this.context.isDataset,
144145
};
145146
this.props.actions.addQueryEditor(newQueryEditor);
146147
}

superset-frontend/src/SqlLab/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface QueryEditor {
6767
southPercent?: number;
6868
updatedAt?: number;
6969
cursorPosition?: CursorPosition;
70+
isDataset?: boolean;
7071
}
7172

7273
export type toastState = {

superset-frontend/src/components/Datasource/DatasourceEditor.jsx

Lines changed: 174 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,6 @@ const StyledButtonWrapper = styled.span`
149149
`}
150150
`;
151151

152-
const sqlTooltipOptions = {
153-
placement: 'topRight',
154-
title: t(
155-
'If changes are made to your SQL query, ' +
156-
'columns in your dataset will be synced when saving the dataset.',
157-
),
158-
};
159-
160152
const checkboxGenerator = (d, onChange) => (
161153
<CheckboxControl value={d} onChange={onChange} />
162154
);
@@ -723,6 +715,22 @@ class DatasourceEditor extends PureComponent {
723715
});
724716
}
725717

718+
getSQLLabUrl() {
719+
const queryParams = new URLSearchParams({
720+
dbid: this.state.datasource.database.id,
721+
sql: this.state.datasource.sql,
722+
name: this.state.datasource.datasource_name,
723+
schema: this.state.datasource.schema,
724+
autorun: true,
725+
isDataset: true,
726+
});
727+
return `/sqllab/?${queryParams.toString()}`;
728+
}
729+
730+
openOnSqlLab() {
731+
window.open(this.getSQLLabUrl(), '_blank', 'noopener,noreferrer');
732+
}
733+
726734
tableChangeAndSyncMetadata() {
727735
this.validate(() => {
728736
this.syncMetadata();
@@ -996,8 +1004,81 @@ class DatasourceEditor extends PureComponent {
9961004
);
9971005
}
9981006

1007+
renderSqlEditorOverlay = () => (
1008+
<div
1009+
css={theme => css`
1010+
position: absolute;
1011+
background: ${theme.colors.secondary.light5};
1012+
align-items: center;
1013+
display: flex;
1014+
height: 100%;
1015+
width: 100%;
1016+
justify-content: center;
1017+
`}
1018+
>
1019+
<div>
1020+
<Loading position="inline-centered" />
1021+
<span
1022+
css={theme => css`
1023+
display: block;
1024+
margin: ${theme.gridUnit * 4}px auto;
1025+
width: fit-content;
1026+
color: ${theme.colors.grayscale.base};
1027+
`}
1028+
>
1029+
{t('We are working on your query')}
1030+
</span>
1031+
</div>
1032+
</div>
1033+
);
1034+
1035+
renderOpenInSqlLabLink(isError = false) {
1036+
return (
1037+
<a
1038+
href={this.getSQLLabUrl()}
1039+
target="_blank"
1040+
rel="noopener noreferrer"
1041+
css={theme => css`
1042+
color: ${isError
1043+
? theme.colors.error.base
1044+
: theme.colors.grayscale.base};
1045+
font-size: ${theme.typography.sizes.s}px;
1046+
text-decoration: underline;
1047+
`}
1048+
>
1049+
{t('Open in SQL lab')}
1050+
</a>
1051+
);
1052+
}
1053+
1054+
renderSqlErrorMessage = () => (
1055+
<>
1056+
<span
1057+
css={theme => css`
1058+
font-size: ${theme.typography.sizes.s}px;
1059+
`}
1060+
>
1061+
{this.props.database?.error && t('Error executing query. ')}
1062+
</span>
1063+
{this.renderOpenInSqlLabLink(true)}
1064+
<span
1065+
css={theme => css`
1066+
font-size: ${theme.typography.sizes.s}px;
1067+
`}
1068+
>
1069+
{t(' to check for details.')}
1070+
</span>
1071+
</>
1072+
);
1073+
9991074
renderSourceFieldset() {
10001075
const { datasource } = this.state;
1076+
const floatingButtonCss = css`
1077+
align-self: flex-end;
1078+
height: 24px;
1079+
padding-left: 6px;
1080+
padding-right: 6px;
1081+
`;
10011082
return (
10021083
<div>
10031084
<EditLockContainer>
@@ -1097,18 +1178,33 @@ class DatasourceEditor extends PureComponent {
10971178
description={t(
10981179
'When specifying SQL, the datasource acts as a view. ' +
10991180
'Superset will use this statement as a subquery while grouping and filtering ' +
1100-
'on the generated parent queries.',
1181+
'on the generated parent queries.' +
1182+
'If changes are made to your SQL query, ' +
1183+
'columns in your dataset will be synced when saving the dataset.',
11011184
)}
11021185
control={
1103-
<TextAreaControl
1104-
language="sql"
1105-
offerEditInModal={false}
1106-
minLines={10}
1107-
maxLines={Infinity}
1108-
readOnly={!this.state.isEditMode}
1109-
resize="both"
1110-
tooltipOptions={sqlTooltipOptions}
1111-
/>
1186+
this.props.database?.isLoading ? (
1187+
<>
1188+
{this.renderSqlEditorOverlay()}
1189+
<TextAreaControl
1190+
language="sql"
1191+
offerEditInModal={false}
1192+
minLines={10}
1193+
maxLines={Infinity}
1194+
readOnly={!this.state.isEditMode}
1195+
resize="both"
1196+
/>
1197+
</>
1198+
) : (
1199+
<TextAreaControl
1200+
language="sql"
1201+
offerEditInModal={false}
1202+
minLines={10}
1203+
maxLines={Infinity}
1204+
readOnly={!this.state.isEditMode}
1205+
resize="both"
1206+
/>
1207+
)
11121208
}
11131209
additionalControl={
11141210
<div
@@ -1120,12 +1216,25 @@ class DatasourceEditor extends PureComponent {
11201216
`}
11211217
>
11221218
<Button
1123-
css={css`
1124-
align-self: flex-end;
1125-
height: 24px;
1126-
padding-left: 6px;
1127-
padding-right: 6px;
1128-
`}
1219+
disabled={this.props.database?.isLoading}
1220+
tooltip={t('Open SQL Lab in a new tab')}
1221+
css={floatingButtonCss}
1222+
size="small"
1223+
>
1224+
<Icons.ExportOutlined
1225+
iconSize="s"
1226+
css={theme => ({
1227+
color: theme.colors.primary.dark1,
1228+
})}
1229+
onClick={() => {
1230+
this.openOnSqlLab();
1231+
}}
1232+
/>
1233+
</Button>
1234+
<Button
1235+
disabled={this.props.database?.isLoading}
1236+
tooltip={t('Run query')}
1237+
css={floatingButtonCss}
11291238
size="small"
11301239
buttonStyle="primary"
11311240
onClick={() => {
@@ -1142,22 +1251,49 @@ class DatasourceEditor extends PureComponent {
11421251
</div>
11431252
}
11441253
errorMessage={
1145-
this.props.database?.error && t('Error executing query.')
1254+
this.props.database?.error && this.renderSqlErrorMessage()
11461255
}
11471256
/>
11481257
{this.props.database?.queryResult && (
1149-
<ResultTable
1150-
data={this.props.database.queryResult.data}
1151-
queryId={this.props.database.queryResult.query.id}
1152-
orderedColumnKeys={this.props.database.queryResult.columns.map(
1153-
col => col.column_name,
1154-
)}
1155-
height={100}
1156-
expandedColumns={
1157-
this.props.database.queryResult.expandedColumns
1158-
}
1159-
allowHTML
1160-
/>
1258+
<>
1259+
<div
1260+
css={theme => css`
1261+
margin-bottom: ${theme.gridUnit * 4}px;
1262+
`}
1263+
>
1264+
<span
1265+
css={theme => css`
1266+
color: ${theme.colors.grayscale.base};
1267+
font-size: ${theme.typography.sizes.s}px;
1268+
`}
1269+
>
1270+
{t(
1271+
'In this view you can preview the first 25 rows. ',
1272+
)}
1273+
</span>
1274+
{this.renderOpenInSqlLabLink()}
1275+
<span
1276+
css={theme => css`
1277+
color: ${theme.colors.grayscale.base};
1278+
font-size: ${theme.typography.sizes.s}px;
1279+
`}
1280+
>
1281+
{t(' to see details.')}
1282+
</span>
1283+
</div>
1284+
<ResultTable
1285+
data={this.props.database?.queryResult.data}
1286+
queryId={this.props.database?.queryResult.query.id}
1287+
orderedColumnKeys={this.props.database?.queryResult.columns.map(
1288+
col => col.column_name,
1289+
)}
1290+
height={100}
1291+
expandedColumns={
1292+
this.props.database?.queryResult.expandedColumns
1293+
}
1294+
allowHTML
1295+
/>
1296+
</>
11611297
)}
11621298
</>
11631299
)}
@@ -1555,8 +1691,7 @@ const mapDispatchToProps = dispatch => ({
15551691
resetQuery: () => dispatch(resetDatabaseState()),
15561692
});
15571693
const mapStateToProps = state => ({
1558-
test: state.queryApi,
1559-
database: state.database,
1694+
database: state?.database,
15601695
});
15611696
export default withToasts(
15621697
connect(mapStateToProps, mapDispatchToProps)(DataSourceComponent),

0 commit comments

Comments
 (0)