refactor: Fix anchor-is-valid lint warnings (#12010)

* Fix anchor-is-valid lint warnings

* Change IconTooltip to use named exports
This commit is contained in:
Michael S. Molina 2020-12-22 04:58:05 -03:00 committed by GitHub
parent e2f676445b
commit 8682c6fc1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 115 additions and 80 deletions

View File

@ -86,7 +86,7 @@ describe('ChangeDatasourceModal', () => {
it('renders confirmation message', async () => {
act(() => {
wrapper.find('.datasource-link').at(0).props().onClick();
wrapper.find('[data-test="datasource-link"]').at(0).props().onClick();
});
await waitForComponentToPaint(wrapper);
@ -95,7 +95,7 @@ describe('ChangeDatasourceModal', () => {
it('changes the datasource', async () => {
act(() => {
wrapper.find('.datasource-link').at(0).props().onClick();
wrapper.find('[data-test="datasource-link"]').at(0).props().onClick();
});
await waitForComponentToPaint(wrapper);

View File

@ -18,22 +18,23 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { Tooltip } from 'src/common/components/Tooltip';
import { IconTooltip } from 'src/components/IconTooltip';
import Link from 'src/components/Link';
describe('Link', () => {
describe('IconTooltip', () => {
const mockedProps = {
tooltip: 'This is a tooltip',
href: 'https://www.airbnb.com',
};
it('renders', () => {
expect(React.isValidElement(<Link>TEST</Link>)).toBe(true);
expect(React.isValidElement(<IconTooltip>TEST</IconTooltip>)).toBe(true);
});
it('renders with props', () => {
expect(React.isValidElement(<Link {...mockedProps}>TEST</Link>)).toBe(true);
expect(
React.isValidElement(<IconTooltip {...mockedProps}>TEST</IconTooltip>),
).toBe(true);
});
it('renders an anchor tag', () => {
const wrapper = shallow(<Link {...mockedProps}>TEST</Link>);
expect(wrapper.find('a')).toExist();
it('renders a tooltip', () => {
const wrapper = shallow(<IconTooltip {...mockedProps}>TEST</IconTooltip>);
expect(wrapper.find(Tooltip)).toExist();
});
});

View File

@ -22,7 +22,7 @@ import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
import Link from 'src/components/Link';
import { IconTooltip } from 'src/components/IconTooltip';
import Fade from 'src/common/components/Fade';
import TableElement from 'src/SqlLab/components/TableElement';
import ColumnElement from 'src/SqlLab/components/ColumnElement';
@ -43,9 +43,9 @@ describe('TableElement', () => {
it('renders with props', () => {
expect(React.isValidElement(<TableElement {...mockedProps} />)).toBe(true);
});
it('has 2 Link elements', () => {
it('has 2 IconTooltip elements', () => {
const wrapper = shallow(<TableElement {...mockedProps} />);
expect(wrapper.find(Link)).toHaveLength(2);
expect(wrapper.find(IconTooltip)).toHaveLength(2);
});
it('has 14 columns', () => {
const wrapper = shallow(<TableElement {...mockedProps} />);
@ -95,7 +95,7 @@ describe('TableElement', () => {
},
);
expect(mockedActions.collapseTable.called).toBe(false);
wrapper.find('.table-name').simulate('click');
wrapper.find('[data-test="collapse"]').hostNodes().simulate('click');
expect(mockedActions.collapseTable.called).toBe(true);
});
it('removes the table', () => {

View File

@ -27,7 +27,7 @@ import { t } from '@superset-ui/core';
import TableView from 'src/components/TableView';
import Button from 'src/components/Button';
import { fDuration } from 'src/modules/dates';
import Link from '../../components/Link';
import { IconTooltip } from '../../components/IconTooltip';
import ResultSet from './ResultSet';
import ModalTrigger from '../../components/ModalTrigger';
import HighlightedSql from './HighlightedSql';
@ -175,9 +175,9 @@ const QueryTable = props => {
let errorTooltip;
if (q.errorMessage) {
errorTooltip = (
<Link tooltip={q.errorMessage}>
<IconTooltip tooltip={q.errorMessage}>
<i className="fa fa-exclamation-circle text-danger" />
</Link>
</IconTooltip>
);
}
q.state = (
@ -188,22 +188,22 @@ const QueryTable = props => {
);
q.actions = (
<div>
<Link
className="fa fa-pencil m-r-3"
<IconTooltip
className="fa fa-pencil m-r-3 pointer"
onClick={() => restoreSql(query)}
tooltip={t(
'Overwrite text in the editor with a query on this table',
)}
placement="top"
/>
<Link
className="fa fa-plus-circle m-r-3"
<IconTooltip
className="fa fa-plus-circle m-r-3 pointer"
onClick={() => openQueryInNewTab(query)}
tooltip={t('Run query in a new tab')}
placement="top"
/>
<Link
className="fa fa-trash m-r-3"
<IconTooltip
className="fa fa-trash m-r-3 pointer"
tooltip={t('Remove query from log')}
onClick={() => removeQuery(query)}
/>

View File

@ -21,7 +21,7 @@ import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light';
import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql';
import github from 'react-syntax-highlighter/dist/cjs/styles/hljs/github';
import Link from '../../components/Link';
import { IconTooltip } from '../../components/IconTooltip';
import ModalTrigger from '../../components/ModalTrigger';
SyntaxHighlighter.registerLanguage('sql', sql);
@ -41,10 +41,9 @@ export default function ShowSQL({
<ModalTrigger
modalTitle={title}
triggerNode={
<Link
<IconTooltip
className="fa fa-eye pull-left m-l-2"
tooltip={tooltipText}
href="#"
/>
}
modalBody={

View File

@ -20,12 +20,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import { ButtonGroup, Collapse, Well } from 'react-bootstrap';
import shortid from 'shortid';
import { t } from '@superset-ui/core';
import { t, styled } from '@superset-ui/core';
import Fade from 'src/common/components/Fade';
import { Tooltip } from 'src/common/components/Tooltip';
import CopyToClipboard from '../../components/CopyToClipboard';
import Link from '../../components/Link';
import { IconTooltip } from '../../components/IconTooltip';
import ColumnElement from './ColumnElement';
import ShowSQL from './ShowSQL';
import ModalTrigger from '../../components/ModalTrigger';
@ -43,6 +43,14 @@ const defaultProps = {
timeout: 500,
};
const StyledSpan = styled.span`
color: ${({ theme }) => theme.colors.primary.dark1};
&: hover {
color: ${({ theme }) => theme.colors.primary.dark2};
}
cursor: pointer;
`;
class TableElement extends React.PureComponent {
constructor(props) {
super(props);
@ -145,7 +153,7 @@ class TableElement extends React.PureComponent {
<pre key={i}>{JSON.stringify(ix, null, ' ')}</pre>
))}
triggerNode={
<Link
<IconTooltip
className="fa fa-key pull-left m-l-2"
tooltip={t('View keys & indexes (%s)', table.indexes.length)}
/>
@ -156,10 +164,10 @@ class TableElement extends React.PureComponent {
return (
<ButtonGroup className="ws-el-controls">
{keyLink}
<Link
<IconTooltip
className={
`fa fa-sort-${!this.state.sortColumns ? 'alpha' : 'numeric'}-asc ` +
'pull-left sort-cols m-l-2'
'pull-left sort-cols m-l-2 pointer'
}
onClick={this.toggleSortColumns}
tooltip={
@ -167,14 +175,13 @@ class TableElement extends React.PureComponent {
? t('Sort columns alphabetically')
: t('Original table column order')
}
href="#"
/>
{table.selectStar && (
<CopyToClipboard
copyNode={
<a aria-label="Copy">
<IconTooltip aria-label="Copy">
<i aria-hidden className="fa fa-clipboard pull-left m-l-2" />
</a>
</IconTooltip>
}
text={table.selectStar}
shouldShowText={false}
@ -188,11 +195,10 @@ class TableElement extends React.PureComponent {
title={t('CREATE VIEW statement')}
/>
)}
<Link
className="fa fa-times table-remove pull-left m-l-2"
<IconTooltip
className="fa fa-times table-remove pull-left m-l-2 pointer"
onClick={this.removeTable}
tooltip={t('Remove table preview')}
href="#"
/>
</ButtonGroup>
);
@ -209,15 +215,15 @@ class TableElement extends React.PureComponent {
title={table.name}
trigger={['hover']}
>
<a
href="#"
<StyledSpan
data-test="collapse"
className="table-name"
onClick={e => {
this.toggleTable(e);
}}
>
<strong>{table.name}</strong>
</a>
</StyledSpan>
</Tooltip>
<div className="pull-right header-right-side">

View File

@ -114,9 +114,14 @@ export default function ErrorAlert({
<strong>{title}</strong>
</LeftSideContent>
{!isExpandable && (
<a href="#" className="link" onClick={() => setIsModalOpen(true)}>
<span
role="button"
tabIndex={0}
className="link"
onClick={() => setIsModalOpen(true)}
>
{t('See More')}
</a>
</span>
)}
</div>
{isExpandable ? (
@ -125,25 +130,27 @@ export default function ErrorAlert({
{body && (
<>
{!isBodyExpanded && (
<a
href="#"
<span
role="button"
tabIndex={0}
className="link"
onClick={() => setIsBodyExpanded(true)}
>
{t('See More')}
</a>
</span>
)}
{isBodyExpanded && (
<>
<br />
{body}
<a
href="#"
<span
role="button"
tabIndex={0}
className="link"
onClick={() => setIsBodyExpanded(false)}
>
{t('See Less')}
</a>
</span>
</>
)}
</>

View File

@ -18,11 +18,11 @@
*/
import React, { ReactNode } from 'react';
import { Tooltip } from 'src/common/components/Tooltip';
import { styled } from '@superset-ui/core';
interface Props {
children?: ReactNode;
className?: string;
href?: string;
onClick?: () => void;
placement?:
| 'bottom'
@ -41,24 +41,29 @@ interface Props {
tooltip?: string | null;
}
const Link = ({
const StyledSpan = styled.span`
color: ${({ theme }) => theme.colors.primary.dark1};
&: hover {
color: ${({ theme }) => theme.colors.primary.dark2};
}
`;
const IconTooltip = ({
children = null,
className = '',
href = '#',
onClick = () => undefined,
placement = 'top',
style = {},
tooltip = null,
}: Props) => {
const link = (
<a
href={href}
const iconTooltip = (
<StyledSpan
onClick={onClick}
style={style}
className={`Link ${className}`}
className={`IconTooltip ${className}`}
>
{children}
</a>
</StyledSpan>
);
if (tooltip) {
return (
@ -69,11 +74,11 @@ const Link = ({
mouseEnterDelay={0.3}
mouseLeaveDelay={0.15}
>
{link}
{iconTooltip}
</Tooltip>
);
}
return link;
return iconTooltip;
};
export default Link;
export { IconTooltip };

View File

@ -265,9 +265,9 @@ class SliceHeaderControls extends React.PureComponent {
triggerNode.closest(SCREENSHOT_NODE_SELECTOR)
}
>
<a id={`slice_${slice.slice_id}-controls`} role="button">
<span id={`slice_${slice.slice_id}-controls`} role="button">
<VerticalDotsTrigger />
</a>
</span>
</NoAnimationDropdown>
);
}

View File

@ -29,13 +29,13 @@ const propTypes = {
export default function FilterFieldItem({ label, isSelected }) {
return (
<a
<span
className={cx('filter-field-item filter-container', {
'is-selected': isSelected,
})}
>
<FormLabel htmlFor={label}>{label}</FormLabel>
</a>
</span>
);
}

View File

@ -35,7 +35,7 @@ function traverse({ currentNode = {}, selectedChartId }) {
return {
...currentNode,
label: (
<a
<span
className={cx(`filter-scope-type ${type.toLowerCase()}`, {
'selected-filter': selectedChartId === value,
})}
@ -46,7 +46,7 @@ function traverse({ currentNode = {}, selectedChartId }) {
</span>
)}
{label}
</a>
</span>
),
children: updatedChildren,
};
@ -54,13 +54,13 @@ function traverse({ currentNode = {}, selectedChartId }) {
return {
...currentNode,
label: (
<a
<span
className={cx(`filter-scope-type ${type.toLowerCase()}`, {
'selected-filter': selectedChartId === value,
})}
>
{label}
</a>
</span>
),
};
}

View File

@ -48,6 +48,14 @@ const StyledForm = styled(Form)`
width: 100%;
`;
const StyledSpan = styled.span`
cursor: pointer;
color: ${({ theme }) => theme.colors.primary.dark1};
&: hover {
color: ${({ theme }) => theme.colors.primary.dark2};
}
`;
const FilterTabs = styled(LineEditableTabs)`
// extra selector specificity:
&.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab {
@ -473,13 +481,13 @@ export function FilterConfigModal({
: getFilterTitle(id)}
</div>
{removedFilters[id] && (
<a
<StyledSpan
role="button"
tabIndex={0}
onClick={() => restoreFilter(id)}
>
{t('Undo?')}
</a>
</StyledSpan>
)}
</FilterTabTitle>
}

View File

@ -67,6 +67,14 @@ const ConfirmModalStyled = styled.div`
}
`;
const StyledSpan = styled.span`
cursor: pointer;
color: ${({ theme }) => theme.colors.primary.dark1};
&: hover {
color: ${({ theme }) => theme.colors.primary.dark2};
}
`;
const TABLE_COLUMNS = [
'name',
'type',
@ -191,13 +199,14 @@ const ChangeDatasourceModal: FunctionComponent<ChangeDatasourceModalProps> = ({
connection: ds.database.database_name,
schema: ds.schema,
name: (
<a
href="#"
<StyledSpan
role="button"
tabIndex={0}
data-test="datasource-link"
onClick={() => selectDatasource({ type: 'table', ...ds })}
className="datasource-link"
>
{ds.table_name}
</a>
</StyledSpan>
),
type: ds.kind,
}));

View File

@ -790,12 +790,12 @@ class DatasourceEditor extends React.PureComponent {
</Fieldset>
{this.allowEditSource && (
<EditLockContainer>
<a href="#" onClick={this.onChangeEditMode}>
<span role="button" tabIndex={0} onClick={this.onChangeEditMode}>
<Icon
color={supersetTheme.colors.grayscale.base}
name={this.state.isEditMode ? 'lock-unlocked' : 'lock-locked'}
/>
</a>
</span>
{!this.state.isEditMode && (
<div>{t('Click the lock to make changes.')}</div>
)}

View File

@ -79,7 +79,7 @@ export default function ExploreActionButtons({
)}
{latestQueryFormData && (
<a
<div
role="button"
tabIndex={0}
onClick={doExportChart}
@ -89,10 +89,10 @@ export default function ExploreActionButtons({
rel="noopener noreferrer"
>
<i className="fa fa-file-code-o" /> .json
</a>
</div>
)}
{latestQueryFormData && (
<a
<div
role="button"
tabIndex={0}
onClick={doExportCSV}
@ -102,7 +102,7 @@ export default function ExploreActionButtons({
rel="noopener noreferrer"
>
<i className="fa fa-file-text-o" /> .csv
</a>
</div>
)}
<ConnectedDisplayQueryButton
chartHeight={chartHeight}