mirror of
https://github.com/apache/superset.git
synced 2026-04-27 12:05:24 +00:00
perf(sqllab): Rendering perf improvement using immutable state (#20877)
* perf(sqllab): Rendering perf improvement using immutable state - keep queryEditors immutable during active state - add unsavedQueryEditor to store all active changes - refactor each component to subscribe the related unsaved editor state only * revert ISaveableDatasource type cast * missing trigger prop * a default of an empty object and optional operator
This commit is contained in:
@@ -17,18 +17,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import configureStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import SaveQuery from 'src/SqlLab/components/SaveQuery';
|
||||
import { databases } from 'src/SqlLab/fixtures';
|
||||
import { initialState, databases } from 'src/SqlLab/fixtures';
|
||||
|
||||
const mockedProps = {
|
||||
query: {
|
||||
queryEditor: {
|
||||
dbId: 1,
|
||||
schema: 'main',
|
||||
sql: 'SELECT * FROM t',
|
||||
},
|
||||
defaultLabel: 'untitled',
|
||||
animation: false,
|
||||
database: databases.result[0],
|
||||
onUpdate: () => {},
|
||||
@@ -43,9 +44,15 @@ const splitSaveBtnProps = {
|
||||
},
|
||||
};
|
||||
|
||||
const middlewares = [thunk];
|
||||
const mockStore = configureStore(middlewares);
|
||||
|
||||
describe('SavedQuery', () => {
|
||||
it('renders a non-split save button when allows_virtual_table_explore is not enabled', () => {
|
||||
render(<SaveQuery {...mockedProps} />, { useRedux: true });
|
||||
render(<SaveQuery {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save/i });
|
||||
|
||||
@@ -53,7 +60,10 @@ describe('SavedQuery', () => {
|
||||
});
|
||||
|
||||
it('renders a save query modal when user clicks save button', () => {
|
||||
render(<SaveQuery {...mockedProps} />, { useRedux: true });
|
||||
render(<SaveQuery {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save/i });
|
||||
userEvent.click(saveBtn);
|
||||
@@ -66,7 +76,10 @@ describe('SavedQuery', () => {
|
||||
});
|
||||
|
||||
it('renders the save query modal UI', () => {
|
||||
render(<SaveQuery {...mockedProps} />, { useRedux: true });
|
||||
render(<SaveQuery {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save/i });
|
||||
userEvent.click(saveBtn);
|
||||
@@ -100,12 +113,15 @@ describe('SavedQuery', () => {
|
||||
it('renders a "save as new" and "update" button if query already exists', () => {
|
||||
const props = {
|
||||
...mockedProps,
|
||||
query: {
|
||||
queryEditor: {
|
||||
...mockedProps.query,
|
||||
remoteId: '42',
|
||||
},
|
||||
};
|
||||
render(<SaveQuery {...props} />, { useRedux: true });
|
||||
render(<SaveQuery {...props} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save/i });
|
||||
userEvent.click(saveBtn);
|
||||
@@ -118,7 +134,10 @@ describe('SavedQuery', () => {
|
||||
});
|
||||
|
||||
it('renders a split save button when allows_virtual_table_explore is enabled', async () => {
|
||||
render(<SaveQuery {...splitSaveBtnProps} />, { useRedux: true });
|
||||
render(<SaveQuery {...splitSaveBtnProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const saveBtn = screen.getByRole('button', { name: /save/i });
|
||||
@@ -130,7 +149,10 @@ describe('SavedQuery', () => {
|
||||
});
|
||||
|
||||
it('renders a save dataset modal when user clicks "save dataset" menu item', async () => {
|
||||
render(<SaveQuery {...splitSaveBtnProps} />, { useRedux: true });
|
||||
render(<SaveQuery {...splitSaveBtnProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const caretBtn = screen.getByRole('button', { name: /caret-down/i });
|
||||
@@ -146,7 +168,10 @@ describe('SavedQuery', () => {
|
||||
});
|
||||
|
||||
it('renders the save dataset modal UI', async () => {
|
||||
render(<SaveQuery {...splitSaveBtnProps} />, { useRedux: true });
|
||||
render(<SaveQuery {...splitSaveBtnProps} />, {
|
||||
useRedux: true,
|
||||
store: mockStore(initialState),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
const caretBtn = screen.getByRole('button', { name: /caret-down/i });
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
import { Row, Col } from 'src/components';
|
||||
import { Input, TextArea } from 'src/components/Input';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
@@ -25,12 +26,16 @@ import { Menu } from 'src/components/Menu';
|
||||
import { Form, FormItem } from 'src/components/Form';
|
||||
import Modal from 'src/components/Modal';
|
||||
import SaveDatasetActionButton from 'src/SqlLab/components/SaveDatasetActionButton';
|
||||
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
|
||||
import {
|
||||
SaveDatasetModal,
|
||||
ISaveableDatasource,
|
||||
} from 'src/SqlLab/components/SaveDatasetModal';
|
||||
import { getDatasourceAsSaveableDataset } from 'src/utils/datasourceUtils';
|
||||
import { QueryEditor, SqlLabRootState } from 'src/SqlLab/types';
|
||||
|
||||
interface SaveQueryProps {
|
||||
query: QueryPayload;
|
||||
defaultLabel: string;
|
||||
queryEditor: QueryEditor;
|
||||
columns: ISaveableDatasource['columns'];
|
||||
onSave: (arg0: QueryPayload) => void;
|
||||
onUpdate: (arg0: QueryPayload) => void;
|
||||
saveQueryWarning: string | null;
|
||||
@@ -76,13 +81,22 @@ const Styles = styled.span`
|
||||
`;
|
||||
|
||||
export default function SaveQuery({
|
||||
query,
|
||||
defaultLabel = t('Undefined'),
|
||||
queryEditor,
|
||||
onSave = () => {},
|
||||
onUpdate,
|
||||
saveQueryWarning = null,
|
||||
database,
|
||||
columns,
|
||||
}: SaveQueryProps) {
|
||||
const query = useSelector<SqlLabRootState, QueryEditor>(
|
||||
({ sqlLab: { unsavedQueryEditor } }) => ({
|
||||
...queryEditor,
|
||||
...(queryEditor.id === unsavedQueryEditor.id && unsavedQueryEditor),
|
||||
columns,
|
||||
}),
|
||||
shallowEqual,
|
||||
);
|
||||
const defaultLabel = query.name || query.description || t('Undefined');
|
||||
const [description, setDescription] = useState<string>(
|
||||
query.description || '',
|
||||
);
|
||||
@@ -100,11 +114,12 @@ export default function SaveQuery({
|
||||
</Menu>
|
||||
);
|
||||
|
||||
const queryPayload = () => ({
|
||||
...query,
|
||||
name: label,
|
||||
description,
|
||||
});
|
||||
const queryPayload = () =>
|
||||
({
|
||||
...query,
|
||||
name: label,
|
||||
description,
|
||||
} as any as QueryPayload);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSaved) setLabel(defaultLabel);
|
||||
|
||||
Reference in New Issue
Block a user