mirror of https://github.com/apache/superset.git
feat(explore): SQL popover in datasource panel (#19308)
* feat(explore): SQL popover in datasource panel * Fix acequire not defined * Rebase and fix tests * Disable highlighting gutter * Use ace-build acequire instead of brace
This commit is contained in:
parent
90dbe8d340
commit
60dcd651f4
|
@ -51,6 +51,7 @@
|
||||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||||
"@vx/responsive": "^0.0.195",
|
"@vx/responsive": "^0.0.195",
|
||||||
"abortcontroller-polyfill": "^1.1.9",
|
"abortcontroller-polyfill": "^1.1.9",
|
||||||
|
"ace-builds": "^1.4.14",
|
||||||
"antd": "^4.9.4",
|
"antd": "^4.9.4",
|
||||||
"array-move": "^2.2.1",
|
"array-move": "^2.2.1",
|
||||||
"babel-plugin-typescript-to-proptypes": "^2.0.0",
|
"babel-plugin-typescript-to-proptypes": "^2.0.0",
|
||||||
|
@ -24403,9 +24404,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ace-builds": {
|
"node_modules/ace-builds": {
|
||||||
"version": "1.4.13",
|
"version": "1.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.14.tgz",
|
||||||
"integrity": "sha512-SOLzdaQkY6ecPKYRDDg+MY1WoGgXA34cIvYJNNoBMGGUswHmlauU2Hy0UL96vW0Fs/LgFbMUjD+6vqzWTldIYQ=="
|
"integrity": "sha512-NBOQlm9+7RBqRqZwimpgquaLeTJFayqb9UEPtTkpC3TkkwDnlsT/TwsCC0svjt9kEZ6G9mH5AEOHSz6Q/HrzQQ=="
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
|
@ -58467,6 +58468,8 @@
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-icons/all-files": "^4.1.0",
|
"@react-icons/all-files": "^4.1.0",
|
||||||
|
"@types/enzyme": "^3.10.5",
|
||||||
|
"@types/react": "*",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
},
|
},
|
||||||
|
@ -58479,10 +58482,11 @@
|
||||||
"@testing-library/react": "^11.2.0",
|
"@testing-library/react": "^11.2.0",
|
||||||
"@testing-library/react-hooks": "^5.0.3",
|
"@testing-library/react-hooks": "^5.0.3",
|
||||||
"@testing-library/user-event": "^12.7.0",
|
"@testing-library/user-event": "^12.7.0",
|
||||||
"@types/enzyme": "^3.10.5",
|
"ace-builds": "^1.4.14",
|
||||||
"@types/react": "*",
|
|
||||||
"antd": "^4.9.4",
|
"antd": "^4.9.4",
|
||||||
|
"brace": "^0.11.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
|
"react-ace": "^9.4.4",
|
||||||
"react-dom": "^16.13.1"
|
"react-dom": "^16.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -59324,6 +59328,7 @@
|
||||||
"prop-types": "^15.6.2"
|
"prop-types": "^15.6.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
"@emotion/react": "^11.4.1",
|
||||||
"@superset-ui/chart-controls": "*",
|
"@superset-ui/chart-controls": "*",
|
||||||
"@superset-ui/core": "*",
|
"@superset-ui/core": "*",
|
||||||
"react": "^16.13.1"
|
"react": "^16.13.1"
|
||||||
|
@ -76177,6 +76182,8 @@
|
||||||
"version": "file:packages/superset-ui-chart-controls",
|
"version": "file:packages/superset-ui-chart-controls",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@react-icons/all-files": "^4.1.0",
|
"@react-icons/all-files": "^4.1.0",
|
||||||
|
"@types/enzyme": "^3.10.5",
|
||||||
|
"@types/react": "*",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
}
|
}
|
||||||
|
@ -79091,9 +79098,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ace-builds": {
|
"ace-builds": {
|
||||||
"version": "1.4.13",
|
"version": "1.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.13.tgz",
|
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.14.tgz",
|
||||||
"integrity": "sha512-SOLzdaQkY6ecPKYRDDg+MY1WoGgXA34cIvYJNNoBMGGUswHmlauU2Hy0UL96vW0Fs/LgFbMUjD+6vqzWTldIYQ=="
|
"integrity": "sha512-NBOQlm9+7RBqRqZwimpgquaLeTJFayqb9UEPtTkpC3TkkwDnlsT/TwsCC0svjt9kEZ6G9mH5AEOHSz6Q/HrzQQ=="
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
|
|
|
@ -111,6 +111,7 @@
|
||||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||||
"@vx/responsive": "^0.0.195",
|
"@vx/responsive": "^0.0.195",
|
||||||
"abortcontroller-polyfill": "^1.1.9",
|
"abortcontroller-polyfill": "^1.1.9",
|
||||||
|
"ace-builds": "^1.4.14",
|
||||||
"antd": "^4.9.4",
|
"antd": "^4.9.4",
|
||||||
"array-move": "^2.2.1",
|
"array-move": "^2.2.1",
|
||||||
"babel-plugin-typescript-to-proptypes": "^2.0.0",
|
"babel-plugin-typescript-to-proptypes": "^2.0.0",
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-icons/all-files": "^4.1.0",
|
"@react-icons/all-files": "^4.1.0",
|
||||||
|
"@types/enzyme": "^3.10.5",
|
||||||
|
"@types/react": "*",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"prop-types": "^15.7.2"
|
"prop-types": "^15.7.2"
|
||||||
},
|
},
|
||||||
|
@ -36,10 +38,11 @@
|
||||||
"@testing-library/react": "^11.2.0",
|
"@testing-library/react": "^11.2.0",
|
||||||
"@testing-library/react-hooks": "^5.0.3",
|
"@testing-library/react-hooks": "^5.0.3",
|
||||||
"@testing-library/user-event": "^12.7.0",
|
"@testing-library/user-event": "^12.7.0",
|
||||||
"@types/enzyme": "^3.10.5",
|
"ace-builds": "^1.4.14",
|
||||||
"@types/react": "*",
|
|
||||||
"antd": "^4.9.4",
|
"antd": "^4.9.4",
|
||||||
|
"brace": "^0.11.1",
|
||||||
"react": "^16.13.1",
|
"react": "^16.13.1",
|
||||||
|
"react-ace": "^9.4.4",
|
||||||
"react-dom": "^16.13.1"
|
"react-dom": "^16.13.1"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
|
|
@ -20,10 +20,10 @@ import React, { useState, ReactNode, useLayoutEffect } from 'react';
|
||||||
import { css, styled, SupersetTheme } from '@superset-ui/core';
|
import { css, styled, SupersetTheme } from '@superset-ui/core';
|
||||||
import { Tooltip } from './Tooltip';
|
import { Tooltip } from './Tooltip';
|
||||||
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
||||||
import InfoTooltipWithTrigger from './InfoTooltipWithTrigger';
|
|
||||||
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
||||||
import { ColumnMeta } from '../types';
|
import { ColumnMeta } from '../types';
|
||||||
import { getColumnLabelText, getColumnTooltipNode } from './labelUtils';
|
import { getColumnLabelText, getColumnTooltipNode } from './labelUtils';
|
||||||
|
import { SQLPopover } from './SQLPopover';
|
||||||
|
|
||||||
export type ColumnOptionProps = {
|
export type ColumnOptionProps = {
|
||||||
column: ColumnMeta;
|
column: ColumnMeta;
|
||||||
|
@ -69,17 +69,7 @@ export function ColumnOption({
|
||||||
{getColumnLabelText(column)}
|
{getColumnLabelText(column)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{hasExpression && <SQLPopover sqlExpression={expression} />}
|
||||||
{hasExpression && (
|
|
||||||
<InfoTooltipWithTrigger
|
|
||||||
className="m-r-5 text-muted"
|
|
||||||
icon="question-circle-o"
|
|
||||||
tooltip={column.expression}
|
|
||||||
label={`expr-${column.column_name}`}
|
|
||||||
placement="top"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{column.is_certified && (
|
{column.is_certified && (
|
||||||
<CertifiedIconWithTooltip
|
<CertifiedIconWithTooltip
|
||||||
metricName={column.metric_name}
|
metricName={column.metric_name}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel';
|
||||||
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
import CertifiedIconWithTooltip from './CertifiedIconWithTooltip';
|
||||||
import Tooltip from './Tooltip';
|
import Tooltip from './Tooltip';
|
||||||
import { getMetricTooltipNode } from './labelUtils';
|
import { getMetricTooltipNode } from './labelUtils';
|
||||||
|
import { SQLPopover } from './SQLPopover';
|
||||||
|
|
||||||
const FlexRowContainer = styled.div`
|
const FlexRowContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -89,13 +90,8 @@ export function MetricOption({
|
||||||
{link}
|
{link}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{showFormula && (
|
{showFormula && metric.expression && (
|
||||||
<InfoTooltipWithTrigger
|
<SQLPopover sqlExpression={metric.expression} />
|
||||||
className="text-muted m-r-5"
|
|
||||||
icon="question-circle-o"
|
|
||||||
tooltip={metric.expression}
|
|
||||||
label={`expr-${metric.metric_name}`}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
{metric.is_certified && (
|
{metric.is_certified && (
|
||||||
<CertifiedIconWithTooltip
|
<CertifiedIconWithTooltip
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* 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 from 'react';
|
||||||
|
import { Popover } from 'antd';
|
||||||
|
import type { PopoverProps } from 'antd/lib/popover';
|
||||||
|
import AceEditor from 'react-ace';
|
||||||
|
import { CalculatorOutlined } from '@ant-design/icons';
|
||||||
|
import { css, styled, useTheme, t } from '@superset-ui/core';
|
||||||
|
import 'ace-builds/src-noconflict/mode-sql';
|
||||||
|
|
||||||
|
const StyledCalculatorIcon = styled(CalculatorOutlined)`
|
||||||
|
${({ theme }) => css`
|
||||||
|
color: ${theme.colors.grayscale.base};
|
||||||
|
font-size: ${theme.typography.sizes.s}px;
|
||||||
|
& svg {
|
||||||
|
margin-left: ${theme.gridUnit}px;
|
||||||
|
margin-right: ${theme.gridUnit}px;
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const SQLPopover = (props: PopoverProps & { sqlExpression: string }) => {
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
content={
|
||||||
|
<AceEditor
|
||||||
|
mode="sql"
|
||||||
|
value={props.sqlExpression}
|
||||||
|
editorProps={{ $blockScrolling: true }}
|
||||||
|
setOptions={{
|
||||||
|
highlightActiveLine: false,
|
||||||
|
highlightGutterLine: false,
|
||||||
|
}}
|
||||||
|
minLines={2}
|
||||||
|
maxLines={6}
|
||||||
|
readOnly
|
||||||
|
wrapEnabled
|
||||||
|
style={{
|
||||||
|
border: `1px solid ${theme.colors.grayscale.light2}`,
|
||||||
|
background: theme.colors.secondary.light5,
|
||||||
|
maxWidth: theme.gridUnit * 100,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
placement="bottomLeft"
|
||||||
|
arrowPointAtCenter
|
||||||
|
title={t('SQL expression')}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<StyledCalculatorIcon />
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
|
@ -20,12 +20,8 @@ import React from 'react';
|
||||||
import { shallow, ShallowWrapper } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { GenericDataType } from '@superset-ui/core';
|
import { GenericDataType } from '@superset-ui/core';
|
||||||
|
|
||||||
import {
|
import { ColumnOption, ColumnOptionProps, ColumnTypeLabel } from '../../src';
|
||||||
ColumnOption,
|
import { SQLPopover } from '../../src/components/SQLPopover';
|
||||||
ColumnOptionProps,
|
|
||||||
ColumnTypeLabel,
|
|
||||||
InfoTooltipWithTrigger,
|
|
||||||
} from '../../src';
|
|
||||||
|
|
||||||
describe('ColumnOption', () => {
|
describe('ColumnOption', () => {
|
||||||
const defaultProps: ColumnOptionProps = {
|
const defaultProps: ColumnOptionProps = {
|
||||||
|
@ -53,8 +49,8 @@ describe('ColumnOption', () => {
|
||||||
expect(lbl).toHaveLength(1);
|
expect(lbl).toHaveLength(1);
|
||||||
expect(lbl.first().text()).toBe('Foo');
|
expect(lbl.first().text()).toBe('Foo');
|
||||||
});
|
});
|
||||||
it('shows 1 InfoTooltipWithTrigger', () => {
|
it('shows SQL Popover trigger', () => {
|
||||||
expect(wrapper.find(InfoTooltipWithTrigger)).toHaveLength(1);
|
expect(wrapper.find(SQLPopover)).toHaveLength(1);
|
||||||
});
|
});
|
||||||
it('shows a label with column_name when no verbose_name', () => {
|
it('shows a label with column_name when no verbose_name', () => {
|
||||||
delete props.column.verbose_name;
|
delete props.column.verbose_name;
|
||||||
|
|
|
@ -51,18 +51,21 @@ describe('MetricOption', () => {
|
||||||
expect(lbl).toHaveLength(1);
|
expect(lbl).toHaveLength(1);
|
||||||
expect(lbl.first().text()).toBe('Foo');
|
expect(lbl.first().text()).toBe('Foo');
|
||||||
});
|
});
|
||||||
it('shows 2 InfoTooltipWithTrigger', () => {
|
it('shows a InfoTooltipWithTrigger', () => {
|
||||||
expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(2);
|
expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
it('shows SQL Popover trigger', () => {
|
||||||
|
expect(wrapper.find('SQLPopover')).toHaveLength(1);
|
||||||
});
|
});
|
||||||
it('shows a label with metric_name when no verbose_name', () => {
|
it('shows a label with metric_name when no verbose_name', () => {
|
||||||
props.metric.verbose_name = '';
|
props.metric.verbose_name = '';
|
||||||
wrapper = shallow(factory(props));
|
wrapper = shallow(factory(props));
|
||||||
expect(wrapper.find('.option-label').first().text()).toBe('foo');
|
expect(wrapper.find('.option-label').first().text()).toBe('foo');
|
||||||
});
|
});
|
||||||
it('shows only 1 InfoTooltipWithTrigger when no warning', () => {
|
it('doesnt show InfoTooltipWithTrigger when no warning', () => {
|
||||||
props.metric.warning_text = '';
|
props.metric.warning_text = '';
|
||||||
wrapper = shallow(factory(props));
|
wrapper = shallow(factory(props));
|
||||||
expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(1);
|
expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
it('sets target="_blank" when openInNewWindow is true', () => {
|
it('sets target="_blank" when openInNewWindow is true', () => {
|
||||||
props.url = 'https://github.com/apache/incubator-superset';
|
props.url = 'https://github.com/apache/incubator-superset';
|
||||||
|
|
|
@ -24,7 +24,6 @@ import {
|
||||||
getByText,
|
getByText,
|
||||||
waitFor,
|
waitFor,
|
||||||
} from 'spec/helpers/testing-library';
|
} from 'spec/helpers/testing-library';
|
||||||
import brace from 'brace';
|
|
||||||
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
import { ThemeProvider, supersetTheme } from '@superset-ui/core';
|
||||||
|
|
||||||
import TemplateParamsEditor from 'src/SqlLab/components/TemplateParamsEditor';
|
import TemplateParamsEditor from 'src/SqlLab/components/TemplateParamsEditor';
|
||||||
|
@ -48,8 +47,6 @@ describe('TemplateParamsEditor', () => {
|
||||||
{ wrapper: ThemeWrapper },
|
{ wrapper: ThemeWrapper },
|
||||||
);
|
);
|
||||||
fireEvent.click(getByText(container, 'Parameters'));
|
fireEvent.click(getByText(container, 'Parameters'));
|
||||||
const spy = jest.spyOn(brace, 'acequire');
|
|
||||||
spy.mockReturnValue({ setCompleters: () => 'foo' });
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(baseElement.querySelector('#ace-editor')).toBeInTheDocument();
|
expect(baseElement.querySelector('#ace-editor')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
|
@ -74,7 +74,6 @@ function TemplateParamsEditor({
|
||||||
syntax.
|
syntax.
|
||||||
</p>
|
</p>
|
||||||
<StyledConfigEditor
|
<StyledConfigEditor
|
||||||
keywords={[]}
|
|
||||||
mode={language}
|
mode={language}
|
||||||
minLines={25}
|
minLines={25}
|
||||||
maxLines={50}
|
maxLines={50}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
TextMode as OrigTextMode,
|
TextMode as OrigTextMode,
|
||||||
} from 'brace';
|
} from 'brace';
|
||||||
import AceEditor, { IAceEditorProps } from 'react-ace';
|
import AceEditor, { IAceEditorProps } from 'react-ace';
|
||||||
|
import { acequire } from 'ace-builds/src-noconflict/ace';
|
||||||
import AsyncEsmComponent, {
|
import AsyncEsmComponent, {
|
||||||
PlaceholderProps,
|
PlaceholderProps,
|
||||||
} from 'src/components/AsyncEsmComponent';
|
} from 'src/components/AsyncEsmComponent';
|
||||||
|
@ -55,7 +56,7 @@ export interface AceCompleterKeyword extends AceCompleterKeywordData {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async loaders to import brace modules. Must manually create call `import(...)`
|
* Async loaders to import brace modules. Must manually create call `import(...)`
|
||||||
* promises because webpack can only analyze asycn imports statically.
|
* promises because webpack can only analyze async imports statically.
|
||||||
*/
|
*/
|
||||||
const aceModuleLoaders = {
|
const aceModuleLoaders = {
|
||||||
'mode/sql': () => import('brace/mode/sql'),
|
'mode/sql': () => import('brace/mode/sql'),
|
||||||
|
@ -101,7 +102,6 @@ export default function AsyncAceEditor(
|
||||||
}: AsyncAceEditorOptions = {},
|
}: AsyncAceEditorOptions = {},
|
||||||
) {
|
) {
|
||||||
return AsyncEsmComponent(async () => {
|
return AsyncEsmComponent(async () => {
|
||||||
const { default: ace } = await import('brace');
|
|
||||||
const { default: ReactAceEditor } = await import('react-ace');
|
const { default: ReactAceEditor } = await import('react-ace');
|
||||||
|
|
||||||
await Promise.all(aceModules.map(x => aceModuleLoaders[x]()));
|
await Promise.all(aceModules.map(x => aceModuleLoaders[x]()));
|
||||||
|
@ -126,7 +126,7 @@ export default function AsyncAceEditor(
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
if (keywords) {
|
if (keywords) {
|
||||||
const langTools = ace.acequire('ace/ext/language_tools');
|
const langTools = acequire('ace/ext/language_tools');
|
||||||
const completer = {
|
const completer = {
|
||||||
getCompletions: (
|
getCompletions: (
|
||||||
editor: AceEditor,
|
editor: AceEditor,
|
||||||
|
|
Loading…
Reference in New Issue