diff --git a/superset-frontend/spec/javascripts/sqllab/TemplateParamsEditor_spec.tsx b/superset-frontend/spec/javascripts/sqllab/TemplateParamsEditor_spec.tsx new file mode 100644 index 0000000000000..ec64b9daf4505 --- /dev/null +++ b/superset-frontend/spec/javascripts/sqllab/TemplateParamsEditor_spec.tsx @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { ReactNode } from 'react'; +import { + render, + fireEvent, + getByText, + waitFor, +} from 'spec/helpers/testing-library'; +import brace from 'brace'; +import { ThemeProvider, supersetTheme } from '@superset-ui/core'; + +import TemplateParamsEditor from 'src/SqlLab/components/TemplateParamsEditor'; + +const ThemeWrapper = ({ children }: { children: ReactNode }) => ( + {children} +); + +describe('TemplateParamsEditor', () => { + it('should render with a title', () => { + const { container } = render( + {}} />, + { wrapper: ThemeWrapper }, + ); + expect(container.querySelector('div[role="button"]')).toBeInTheDocument(); + }); + + it('should open a modal with the ace editor', async () => { + const { container, baseElement } = render( + {}} />, + { wrapper: ThemeWrapper }, + ); + fireEvent.click(getByText(container, 'Parameters')); + const spy = jest.spyOn(brace, 'acequire'); + spy.mockReturnValue({ setCompleters: () => 'foo' }); + await waitFor(() => { + expect(baseElement.querySelector('#brace-editor')).toBeInTheDocument(); + }); + }); +}); diff --git a/superset-frontend/src/SqlLab/components/TemplateParamsEditor.jsx b/superset-frontend/src/SqlLab/components/TemplateParamsEditor.tsx similarity index 75% rename from superset-frontend/src/SqlLab/components/TemplateParamsEditor.jsx rename to superset-frontend/src/SqlLab/components/TemplateParamsEditor.tsx index 50d26db7295d1..5d30a88f31053 100644 --- a/superset-frontend/src/SqlLab/components/TemplateParamsEditor.jsx +++ b/superset-frontend/src/SqlLab/components/TemplateParamsEditor.tsx @@ -17,7 +17,6 @@ * under the License. */ import React, { useState, useEffect } from 'react'; -import PropTypes from 'prop-types'; import Badge from 'src/components/Badge'; import { t, styled } from '@superset-ui/core'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; @@ -26,17 +25,7 @@ import { debounce } from 'lodash'; import ModalTrigger from 'src/components/ModalTrigger'; import { ConfigEditor } from 'src/components/AsyncAceEditor'; import { FAST_DEBOUNCE } from 'src/constants'; - -const propTypes = { - onChange: PropTypes.func, - code: PropTypes.string, - language: PropTypes.oneOf(['yaml', 'json']), -}; - -const defaultProps = { - onChange: () => {}, - code: '{}', -}; +import { Tooltip } from 'src/common/components/Tooltip'; const StyledConfigEditor = styled(ConfigEditor)` &.ace_editor { @@ -44,8 +33,16 @@ const StyledConfigEditor = styled(ConfigEditor)` } `; -function TemplateParamsEditor({ code, language, onChange }) { - const [parsedJSON, setParsedJSON] = useState(); +function TemplateParamsEditor({ + code = '{}', + language, + onChange = () => {}, +}: { + code: string; + language: 'yaml' | 'json'; + onChange: () => void; +}) { + const [parsedJSON, setParsedJSON] = useState({}); const [isValid, setIsValid] = useState(true); useEffect(() => { @@ -53,7 +50,7 @@ function TemplateParamsEditor({ code, language, onChange }) { setParsedJSON(JSON.parse(code)); setIsValid(true); } catch { - setParsedJSON({}); + setParsedJSON({} as any); setIsValid(false); } }, [code]); @@ -96,25 +93,29 @@ function TemplateParamsEditor({ code, language, onChange }) { - {`${t('Parameters')} `} - - {!isValid && ( - - )} - + +
+ {`${t('Parameters')} `} + + {!isValid && ( + + )} +
+
} modalBody={modalBody} /> ); } -TemplateParamsEditor.propTypes = propTypes; -TemplateParamsEditor.defaultProps = defaultProps; - export default TemplateParamsEditor; diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.js b/superset-frontend/src/SqlLab/reducers/getInitialState.js index bd74580d0c021..8052476f9bd61 100644 --- a/superset-frontend/src/SqlLab/reducers/getInitialState.js +++ b/superset-frontend/src/SqlLab/reducers/getInitialState.js @@ -74,13 +74,13 @@ export default function getInitialState({ id: id.toString(), loaded: true, title: activeTab.label, - sql: activeTab.sql, - selectedText: null, + sql: activeTab.sql || undefined, + selectedText: undefined, latestQueryId: activeTab.latest_query ? activeTab.latest_query.id : null, autorun: activeTab.autorun, - templateParams: activeTab.template_params, + templateParams: activeTab.template_params || undefined, dbId: activeTab.database_id, functionNames: [], schema: activeTab.schema, diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.test.ts b/superset-frontend/src/SqlLab/reducers/getInitialState.test.ts index e08443b0598d4..94a0c6a85ec6a 100644 --- a/superset-frontend/src/SqlLab/reducers/getInitialState.test.ts +++ b/superset-frontend/src/SqlLab/reducers/getInitialState.test.ts @@ -36,8 +36,19 @@ const apiData = { username: 'some name', }, }; +const apiDataWithTabState = { + ...apiData, + tab_state_ids: [{ id: 1 }], + active_tab: { id: 1, table_schemas: [] }, +}; describe('getInitialState', () => { it('should output the user that is passed in', () => { expect(getInitialState(apiData).sqlLab.user.userId).toEqual(1); }); + it('should return undefined instead of null for templateParams', () => { + expect( + getInitialState(apiDataWithTabState).sqlLab.queryEditors[0] + .templateParams, + ).toBeUndefined(); + }); }); diff --git a/superset-frontend/src/components/AsyncAceEditor/index.tsx b/superset-frontend/src/components/AsyncAceEditor/index.tsx index 0d394d9f3d492..2a5683574a85d 100644 --- a/superset-frontend/src/components/AsyncAceEditor/index.tsx +++ b/superset-frontend/src/components/AsyncAceEditor/index.tsx @@ -119,6 +119,8 @@ export default function AsyncAceEditor( mode = inferredMode, theme = inferredTheme, tabSize = defaultTabSize, + defaultValue = '', + value = '', ...props }, ref, @@ -150,6 +152,8 @@ export default function AsyncAceEditor( mode={mode} theme={theme} tabSize={tabSize} + defaultValue={defaultValue} + value={value || ''} {...props} /> );