Unverified Commit d3dd9617 authored by Marcos Iglesias's avatar Marcos Iglesias Committed by GitHub

build: Configures ESLint and Prettier (#477)

* Updates eslint packages

* Merging commits

* Updating

* Updating

* Updates

* Fixing Query block issues

* Fixes anchor issue and bracket values type errors

* Formatting
parent df68427f
{
"extends": "airbnb",
"parserOptions":{
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
},
"globals": {
"document": true,
},
"rules": {
"prefer-template": 0,
"new-cap": 0,
"no-restricted-syntax": 0,
"guard-for-in": 0,
"prefer-arrow-callback": 0,
"func-names": 0,
"react/jsx-no-bind": 0,
"no-confusing-arrow": 0,
"jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/anchor-has-content": 0,
"jsx-a11y/anchor-is-valid": [ "error", {
"components": [ "Link" ],
"specialLink": [ "to", "hrefLeft", "hrefRight" ],
"aspects": [ "noHref", "invalidHref", "preferButton" ]
}],
"react/require-default-props": 0,
"no-plusplus": 0,
"no-mixed-operators": 0,
"no-continue": 0,
"no-bitwise": 0,
"no-undef": 0,
"no-multi-assign": 0,
"react/no-array-index-key": 0,
"no-restricted-properties": 0,
"no-prototype-builtins": 0,
"jsx-a11y/href-no-hash": 0,
"react/forbid-prop-types": 0,
"class-methods-use-this": 0,
"import/extensions": 0,
"import/no-named-as-default": 0,
"import/no-extraneous-dependencies": 0,
"import/no-unresolved": 0,
"import/prefer-default-export": 0,
"react/no-unescaped-entities": 0,
"react/no-unused-prop-types": 0,
"react/no-string-refs": 0,
"indent": 0,
"no-multi-spaces": 0,
"padded-blocks": 0,
}
}
......@@ -7,6 +7,7 @@ $loading-curve: cubic-bezier(0.45, 0, 0.15, 1);
0% {
background-position: 100% 0;
}
100% {
background-position: 0 0;
}
......
......@@ -11,13 +11,13 @@ $data-stores: (
druid: '/static/images/icons/logo-druid.svg',
presto: '/static/images/icons/logo-presto.svg',
postgres: '/static/images/icons/logo-postgres.svg',
redshift: '/static/images/icons/logo-redshift.svg'
redshift: '/static/images/icons/logo-redshift.svg',
);
// Map of Dashboard names and icon paths
$dashboards: (
dashboard: '/static/images/icons/dashboard.svg',
mode: '/static/images/icons/logo-mode.svg'
mode: '/static/images/icons/logo-mode.svg',
);
// Map of User names and icon paths
......@@ -35,7 +35,6 @@ $users: (
}
span.icon {
// Generate Icons
@include iconBackgrounds($data-stores);
@include iconBackgrounds($dashboards);
......
......@@ -42,8 +42,14 @@ $resource-header-height: 84px;
li {
display: inline;
&::after { content: '\00A0\2022\00A0'; }
&:last-child::after { content: ''; }
&::after {
content: '\00A0\2022\00A0';
}
&:last-child::after {
content: '';
}
}
}
......
@import 'variables';
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
h1,
h2,
......
......@@ -13,13 +13,13 @@ $brand-primary: $brand-color-4 !default;
/* Scaffolding */
$body-bg: $white !default;
$body-bg-secondary: $gray0 !default;
$body-bg-tertiary: $gray5 !default;
$body-bg-dark: $gray100 !default;
$divider: $gray15 !default;
$stroke: $gray20 !default;
$stroke-light: $gray10 !default;
$stroke-focus: $gray60 !default;
$stroke-underline: $gray40 !default;
$body-bg-tertiary: $gray5 !default;
$body-bg-dark: $gray100 !default;
$divider: $gray15 !default;
$stroke: $gray20 !default;
$stroke-light: $gray10 !default;
$stroke-focus: $gray60 !default;
$stroke-underline: $gray40 !default;
// Typography
$text-primary: $gray100 !default;
......
@import 'bootstrap-custom';
@import 'animations';
@import 'avatars';
@import 'buttons';
......
declare var require: {
declare const require: {
<T>(path: string): T;
(paths: string[], callback: (...modules: any[]) => void): void;
ensure: (
......
module.exports = {
coverageThreshold: {
'./js/config': {
branches: 90,
functions: 90,
lines: 90,
statements: 90,
},
'./js/components': {
branches: 60, // 75
functions: 65, // 75
lines: 65, // 75
statements: 70, // 75
},
'./js/ducks': {
branches: 75,
functions: 80,
lines: 80,
statements: 85,
},
'./js/fixtures': {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
'./js/config': {
branches: 90,
functions: 90,
lines: 90,
statements: 90,
},
'./js/components': {
branches: 60, // 75
functions: 65, // 75
lines: 65, // 75
statements: 70, // 75
},
'./js/ducks': {
branches: 75,
functions: 80,
lines: 80,
statements: 85,
},
'./js/fixtures': {
branches: 100,
functions: 100,
lines: 100,
statements: 100,
},
},
roots: [
'<rootDir>/js',
],
setupFiles: [
'<rootDir>/test-setup.ts',
],
roots: ['<rootDir>/js'],
setupFiles: ['<rootDir>/test-setup.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.js$': 'babel-jest',
},
testRegex: '(test|spec)\\.(j|t)sx?$',
moduleDirectories: ['node_modules', 'js'],
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx',
'json',
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
moduleNameMapper: {
'^.+\\.(css|scss)$': '<rootDir>/node_modules/jest-css-modules',
},
......
import * as React from 'react';
import * as DocumentTitle from 'react-document-title';
import SanitizedHTML from 'react-sanitized-html';
import { shallow } from 'enzyme';
import globalState from 'fixtures/globalState';
import {
AnnouncementPage,
AnnouncementPageProps,
mapDispatchToProps,
mapStateToProps,
} from '../';
import globalState from 'fixtures/globalState';
} from '..';
describe('AnnouncementPage', () => {
let props: AnnouncementPageProps;
......
......@@ -3,8 +3,8 @@ import * as DocumentTitle from 'react-document-title';
import { shallow } from 'enzyme';
import { BrowsePage } from '../';
import TagsList from 'components/common/TagsList';
import { BrowsePage } from '..';
describe('BrowsePage', () => {
const setup = () => {
......
......@@ -2,7 +2,7 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import ChartList, { ChartListProps } from './';
import ChartList, { ChartListProps } from '.';
describe('ChartList', () => {
const setup = (propOverrides?: Partial<ChartListProps>) => {
......
......@@ -8,7 +8,7 @@ export interface ChartListProps {
class ChartList extends React.Component<ChartListProps> {
render() {
const charts = this.props.charts;
const { charts } = this.props;
if (charts.length === 0) {
return null;
}
......
......@@ -5,7 +5,7 @@ import { mount } from 'enzyme';
import Linkify from 'react-linkify';
import { ImagePreview, ImagePreviewProps } from './';
import { ImagePreview, ImagePreviewProps } from '.';
import ShimmeringDashboardLoader from '../ShimmeringDashboardLoader';
......@@ -131,23 +131,21 @@ describe('ImagePreview', () => {
it('should open a modal', () => {
const expected = 1;
let actual;
wrapper.find('.preview-button').simulate('click');
actual = wrapper.find(Modal).length;
const actual = wrapper.find(Modal).length;
expect(actual).toEqual(expected);
});
describe('when closing the modal', () => {
it('should remove the modal markup', () => {
const expected = 0;
let actual;
wrapper.find('.preview-button').simulate('click');
wrapper.find('.modal-header .close').simulate('click');
actual = wrapper.find(Modal).length;
const actual = wrapper.find(Modal).length;
expect(actual).toEqual(expected);
});
});
......
......@@ -37,7 +37,7 @@ const PreviewModal = ({ imageSrc, onClose }: PreviewModalProps) => {
scrollable="true"
className="dashboard-preview-modal"
>
<Modal.Header closeButton={true}>
<Modal.Header closeButton>
<Modal.Title className="text-center">
{Constants.DASHBOARD_PREVIEW_MODAL_TITLE}
</Modal.Title>
......@@ -106,7 +106,9 @@ export class ImagePreview extends React.Component<
<Linkify
className="body-placeholder"
properties={{ target: '_blank', rel: 'noopener noreferrer' }}
>{`${Constants.ERROR_MESSAGE} ${redirectUrl}`}</Linkify>
>
{`${Constants.ERROR_MESSAGE} ${redirectUrl}`}
</Linkify>
)}
{isModalVisible && (
<PreviewModal
......
import * as React from 'react';
import { shallow } from 'enzyme';
import QueryList, { QueryListProps } from './';
import QueryList, { QueryListProps } from '.';
import QueryListItem from '../QueryListItem';
import { ResourceType } from 'interfaces';
......
......@@ -8,5 +8,4 @@ $min-item-height: 54px;
.query-list-item.list-group-item {
min-height: $min-item-height;
padding: 0 $spacer-3;
}
......@@ -3,15 +3,19 @@ import { CopyBlock, atomOneLight } from 'react-code-blocks';
const LANGUAGE = 'sql';
const CodeBlock = ({ text }) => {
type CodeBlockProps = {
text: string;
};
const CodeBlock: React.SFC<CodeBlockProps> = ({ text }: CodeBlockProps) => {
return (
<CopyBlock
text={text}
language={LANGUAGE}
theme={atomOneLight}
showLineNumbers={false}
wrapLines={true}
codeBlock={true}
wrapLines
codeBlock
/>
);
};
......
......@@ -2,7 +2,7 @@ import * as React from 'react';
import { mount } from 'enzyme';
import QueryListItem, { QueryListItemProps } from './';
import QueryListItem, { QueryListItemProps } from '.';
const setup = (propOverrides?: Partial<QueryListItemProps>) => {
const props: QueryListItemProps = {
......@@ -85,10 +85,9 @@ describe('QueryListItem', () => {
it('should render the expanded content', () => {
const { wrapper, props } = setup();
const expected = 1;
let actual;
wrapper.find('.query-list-header').simulate('click');
actual = wrapper.find('.query-list-expanded-content').length;
const actual = wrapper.find('.query-list-expanded-content').length;
expect(actual).toEqual(expected);
});
......@@ -97,11 +96,10 @@ describe('QueryListItem', () => {
it('should hide the expanded content', () => {
const { wrapper, props } = setup();
const expected = 0;
let actual;
wrapper.find('.query-list-header').simulate('click');
wrapper.find('.query-list-header').simulate('click');
actual = wrapper.find('.query-list-expanded-content').length;
const actual = wrapper.find('.query-list-expanded-content').length;
expect(actual).toEqual(expected);
});
......
......@@ -69,21 +69,16 @@ const QueryListItem = ({ name, text, url }: QueryListItemProps) => {
const key = `key:${name}`;
return (
<li
className="list-group-item query-list-item clickable"
role="tab"
id={key}
>
<a
<li className="list-group-item query-list-item" role="tab" id={key}>
<button
className="query-list-header"
aria-expanded={isExpanded}
aria-controls={key}
role="button"
href="#"
type="button"
onClick={toggleExpand}
>
<p className="query-list-item-name column-name">{name}</p>
</a>
</button>
{isExpanded && (
<div className="query-list-expanded-content">
<label className="query-list-query-label section-title">
......
......@@ -35,23 +35,34 @@ $shimmer-loader-lines: 1, 2, 3, 4, 5, 6;
.query-list-header {
display: block;
text-align: left;
background: $btn-default-bg;
border: 0;
width: 100%;
padding-left: $spacer-3;
padding-right: $spacer-3;
&:hover,
&:focus {
text-decoration: none;
outline: 0;
}
}
.query-list-item-name {
margin: 0;
line-height: $min-item-height;
}
.query-list-expanded-content {
padding-bottom: $spacer-3;
padding: 0 $spacer-3 $spacer-3 $spacer-3;
}
.query-list-query-label {
color: $text-tertiary;
display: block;
}
.query-list-query-content {
border-radius: $query-content-border-radius;
border: 1px solid $stroke;
......@@ -75,6 +86,7 @@ $shimmer-loader-lines: 1, 2, 3, 4, 5, 6;
button {
@include code-block-action-button-style;
display: none;
padding: 6px;
margin: $code-block-button-spacer (2 * $code-block-button-spacer) 0 0;
......@@ -94,6 +106,7 @@ $shimmer-loader-lines: 1, 2, 3, 4, 5, 6;
.query-list-query-link {
@include code-block-action-button-style;
display: none;
line-height: $code-block-button-size;
text-align: center;
......
import * as React from 'react';
import { shallow } from 'enzyme';
import ShimmeringDashboardLoader from './';
import ShimmeringDashboardLoader from '.';
const setup = () => {
const wrapper = shallow(<ShimmeringDashboardLoader />);
......
......@@ -10,21 +10,19 @@ import BookmarkIcon from 'components/common/Bookmark/BookmarkIcon';
import Flag from 'components/common/Flag';
import ResourceList from 'components/common/ResourceList';
import TabsComponent from 'components/common/TabsComponent';
import { dashboardMetadata } from 'fixtures/metadata/dashboard';
import { NO_TIMESTAMP_TEXT } from 'components/constants';
import * as LogUtils from 'utils/logUtils';
import { ResourceType } from 'interfaces';
import ChartList from './ChartList';
import ImagePreview from './ImagePreview';
import { DashboardPage, DashboardPageProps, MatchProps } from './';
import { DashboardPage, DashboardPageProps, MatchProps } from '.';
import { getMockRouterProps } from '../../fixtures/mockRouter';
import { dashboardMetadata } from 'fixtures/metadata/dashboard';
import { NO_TIMESTAMP_TEXT } from 'components/constants';
import * as Constants from './constants';
import * as LogUtils from 'utils/logUtils';
import { ResourceType } from 'interfaces';
const MOCK_DISPLAY_NAME = 'displayName';
const MOCK_ICON_CLASS = 'dashboard-icon';
......
......@@ -17,7 +17,6 @@ import { GetDashboardRequest } from 'ducks/dashboard/types';
import { GlobalState } from 'ducks/rootReducer';
import { logClick } from 'ducks/utilMethods';
import { DashboardMetadata } from 'interfaces/Dashboard';
import ImagePreview from './ImagePreview';
import QueryList from 'components/DashboardPage/QueryList';
import ChartList from 'components/DashboardPage/ChartList';
import { formatDateTimeShort } from 'utils/dateUtils';
......@@ -39,6 +38,7 @@ import { getSourceDisplayName, getSourceIconClass } from 'config/config-utils';
import { getLoggingParams } from 'utils/logUtils';
import { NO_TIMESTAMP_TEXT } from 'components/constants';
import ImagePreview from './ImagePreview';
import './styles.scss';
......@@ -135,7 +135,7 @@ export class DashboardPage extends React.Component<
title: `Queries (${this.props.dashboard.queries.length})`,
});
return <TabsComponent tabs={tabInfo} defaultTab={'tables'} />;
return <TabsComponent tabs={tabInfo} defaultTab="tables" />;
}
render() {
......@@ -206,7 +206,7 @@ export class DashboardPage extends React.Component<
<section className="left-panel">
<EditableSection
title="Description"
readOnly={true}
readOnly
editUrl={dashboard.url}
editText={`${EDIT_DESC_TEXT} ${getSourceDisplayName(
dashboard.product,
......
......@@ -2,13 +2,9 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import AbstractFeedbackForm, {
DispatchFromProps,
StateFromProps,
} from '../../FeedbackForm';
import { GlobalState } from 'ducks/rootReducer';
import { submitFeedback, resetFeedback } from 'ducks/feedback/reducer';
import AbstractFeedbackForm, { DispatchFromProps, StateFromProps } from '..';
import {
BUG_SUMMARY_LABEL,
......@@ -32,7 +28,7 @@ export class BugReportFeedbackForm extends AbstractFeedbackForm {
autoComplete="off"
name="subject"
className="form-control"
required={true}
required
placeholder={SUBJECT_PLACEHOLDER}
/>
</div>
......@@ -41,7 +37,7 @@ export class BugReportFeedbackForm extends AbstractFeedbackForm {
<textarea
name="bug-summary"
className="form-control"
required={true}
required
rows={3}
maxLength={2000}
placeholder={BUG_SUMMARY_PLACEHOLDER}
......@@ -53,7 +49,7 @@ export class BugReportFeedbackForm extends AbstractFeedbackForm {
name="repro-steps"
className="form-control"
rows={5}
required={true}
required
maxLength={2000}
placeholder={REPRO_STEPS_PLACEHOLDER}
/>
......
......@@ -6,11 +6,6 @@ import AbstractFeedbackForm, {
FeedbackFormProps,
} from 'components/Feedback/FeedbackForm';
import { SendingState } from 'interfaces';
import {
BugReportFeedbackForm,
mapDispatchToProps,
mapStateToProps,
} from '../';
import {
BUG_SUMMARY_LABEL,
BUG_SUMMARY_PLACEHOLDER,
......@@ -22,6 +17,7 @@ import {
} from 'components/Feedback/constants';
import globalState from 'fixtures/globalState';
import { BugReportFeedbackForm, mapDispatchToProps, mapStateToProps } from '..';
describe('BugReportFeedbackForm', () => {
const setup = () => {
......
......@@ -2,13 +2,9 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import AbstractFeedbackForm, {
DispatchFromProps,
StateFromProps,
} from '../../FeedbackForm';
import { GlobalState } from 'ducks/rootReducer';
import { submitFeedback, resetFeedback } from 'ducks/feedback/reducer';
import AbstractFeedbackForm, { DispatchFromProps, StateFromProps } from '..';
import {
COMMENTS_PLACEHOLDER,
......
......@@ -6,7 +6,6 @@ import AbstractFeedbackForm, {
FeedbackFormProps,
} from 'components/Feedback/FeedbackForm';
import { SendingState } from 'interfaces';
import { RatingFeedbackForm, mapDispatchToProps, mapStateToProps } from '../';
import {
COMMENTS_PLACEHOLDER,
RATING_LABEL,
......@@ -16,6 +15,7 @@ import {
} from 'components/Feedback/constants';
import globalState from 'fixtures/globalState';
import { RatingFeedbackForm, mapDispatchToProps, mapStateToProps } from '..';
describe('RatingFeedbackForm', () => {
const setup = () => {
......
......@@ -2,13 +2,9 @@ import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import AbstractFeedbackForm, {
DispatchFromProps,
StateFromProps,
} from '../../FeedbackForm';
import { GlobalState } from 'ducks/rootReducer';
import { submitFeedback, resetFeedback } from 'ducks/feedback/reducer';
import AbstractFeedbackForm, { DispatchFromProps, StateFromProps } from '..';
import {
FEATURE_SUMMARY_LABEL,
......@@ -32,7 +28,7 @@ export class RequestFeedbackForm extends AbstractFeedbackForm {
autoComplete="off"
name="subject"
className="form-control"
required={true}
required
placeholder={SUBJECT_PLACEHOLDER}
/>
</div>
......@@ -42,7 +38,7 @@ export class RequestFeedbackForm extends AbstractFeedbackForm {
name="feature-summary"
className="form-control"
rows={3}
required={true}
required
maxLength={2000}
placeholder={FEATURE_SUMMARY_PLACEHOLDER}
/>
......@@ -53,7 +49,7 @@ export class RequestFeedbackForm extends AbstractFeedbackForm {
name="value-prop"
className="form-control"
rows={5}
required={true}
required
maxLength={2000}
placeholder={PROPOSITION_PLACEHOLDER}
/>
......
......@@ -6,7 +6,6 @@ import AbstractFeedbackForm, {
FeedbackFormProps,
} from 'components/Feedback/FeedbackForm';
import { SendingState } from 'interfaces';
import { RequestFeedbackForm, mapDispatchToProps, mapStateToProps } from '../';
import {
FEATURE_SUMMARY_LABEL,
FEATURE_SUMMARY_PLACEHOLDER,
......@@ -18,6 +17,7 @@ import {
} from 'components/Feedback/constants';
import globalState from 'fixtures/globalState';
import { RequestFeedbackForm, mapDispatchToProps, mapStateToProps } from '..';
describe('RequestFeedbackForm', () => {
const setup = () => {
......
import * as React from 'react';
import { Button, Panel } from 'react-bootstrap';
import BugReportFeedbackForm from './FeedbackForm/BugReportFeedbackForm';
import RatingFeedbackForm from './FeedbackForm/RatingFeedbackForm';
import RequestFeedbackForm from './FeedbackForm/RequestFeedbackForm';
import { Button, Panel } from 'react-bootstrap';
import {
BUG_REPORT_TEXT,
BUTTON_CLOSE_TEXT,
......
import * as React from 'react';
import { shallow } from 'enzyme';
import { Footer, FooterProps, mapDispatchToProps, mapStateToProps } from '.';
import globalState from 'fixtures/globalState';
import * as DateUtils from 'utils/dateUtils';
import { Footer, FooterProps, mapDispatchToProps, mapStateToProps } from '.';
const MOCK_DATE_STRING = 'Jan 1 2000 at 0:00:00 am';
jest.spyOn(DateUtils, 'formatDateTimeLong').mockReturnValue(MOCK_DATE_STRING);
......
......@@ -31,7 +31,7 @@ export class Footer extends React.Component<FooterProps> {
render() {
let content;
if (!!this.props.lastIndexed) {
if (this.props.lastIndexed) {
content = (
<div>{`Amundsen was last indexed on ${this.generateDateTimeString()}`}</div>
);
......
......@@ -2,8 +2,6 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import { mapDispatchToProps, HomePage, HomePageProps } from '.';
import Breadcrumb from 'components/common/Breadcrumb';
import MyBookmarks from 'components/common/Bookmark/MyBookmarks';
import PopularTables from 'components/common/PopularTables';
......@@ -11,6 +9,7 @@ import SearchBar from 'components/common/SearchBar';
import TagsList from 'components/common/TagsList';
import { getMockRouterProps } from 'fixtures/mockRouter';
import { mapDispatchToProps, HomePage, HomePageProps } from '.';
describe('HomePage', () => {
const setup = (propOverrides?: Partial<HomePageProps>) => {
......
......@@ -5,7 +5,6 @@ import { RouteComponentProps } from 'react-router';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
import { SEARCH_BREADCRUMB_TEXT } from './constants';
import MyBookmarks from 'components/common/Bookmark/MyBookmarks';
import Breadcrumb from 'components/common/Breadcrumb';
......@@ -14,6 +13,7 @@ import { resetSearchState } from 'ducks/search/reducer';
import { UpdateSearchStateReset } from 'ducks/search/types';
import SearchBar from 'components/common/SearchBar';
import TagsList from 'components/common/TagsList';
import { SEARCH_BREADCRUMB_TEXT } from './constants';
export interface DispatchFromProps {
searchReset: () => UpdateSearchStateReset;
......
......@@ -6,20 +6,23 @@ import { shallow } from 'enzyme';
import { Dropdown, MenuItem } from 'react-bootstrap';
import { Link, NavLink } from 'react-router-dom';
import { NavBar, NavBarProps, mapStateToProps } from '.';
import { getMockRouterProps } from 'fixtures/mockRouter';
import Feedback from 'components/Feedback';
import SearchBar from 'components/common/SearchBar';
import { logClick } from 'ducks/utilMethods';
import AppConfig from 'config/config';
import globalState from 'fixtures/globalState';
import { NavBar, NavBarProps, mapStateToProps } from '.';
jest.mock('ducks/utilMethods', () => {
return jest.fn().mockImplementation(() => {
return { logClick: jest.fn() };
});
});
import AppConfig from 'config/config';
AppConfig.logoPath = '/test';
AppConfig.navLinks = [
{
......@@ -40,8 +43,6 @@ AppConfig.navLinks = [
AppConfig.indexUsers.enabled = true;
AppConfig.mailClientFeatures.feedbackEnabled = true;
import globalState from 'fixtures/globalState';
describe('NavBar', () => {
const setup = (
propOverrides?: Partial<NavBarProps>,
......
......@@ -73,7 +73,7 @@ export class NavBar extends React.Component<NavBarProps> {
<div className="row">
<div className="nav-bar">
<div id="nav-bar-left" className="nav-bar-left">
<Link to={`/`}>
<Link to="/">
{AppConfig.logoPath && (
<img
id="logo-icon"
......@@ -89,15 +89,15 @@ export class NavBar extends React.Component<NavBarProps> {
{this.generateNavLinks(AppConfig.navLinks)}
{feedbackEnabled() && <Feedback />}
{this.props.loggedInUser && indexUsersEnabled() && (
<Dropdown id="user-dropdown" pullRight={true}>
<Dropdown id="user-dropdown" pullRight>
<Dropdown.Toggle
noCaret={true}
noCaret
className="nav-bar-avatar avatar-dropdown"
>
<Avatar
name={this.props.loggedInUser.display_name}
size={32}
round={true}
round
/>
</Dropdown.Toggle>
<Dropdown.Menu className="profile-menu">
......@@ -123,7 +123,7 @@ export class NavBar extends React.Component<NavBarProps> {
<Avatar
name={this.props.loggedInUser.display_name}
size={32}
round={true}
round
/>
</div>
)}
......
......@@ -37,6 +37,7 @@ export class PreferencesPage extends React.Component<
selectedPreference: ALL_PREFERENCE,
};
}
changePreference = (newPreference) => {
this.setState({
selectedPreference: newPreference,
......@@ -54,20 +55,14 @@ export class PreferencesPage extends React.Component<
<PreferenceGroup
onClick={this.changePreference}
preferenceValue={ALL_PREFERENCE}
selected={
this.state.selectedPreference === ALL_PREFERENCE ? true : false
}
selected={this.state.selectedPreference === ALL_PREFERENCE}
title={ALL_NOTIFICATIONS_TITLE}
subtitle={ALL_NOTIFICATIONS_SUBTITLE}
/>
<PreferenceGroup
onClick={this.changePreference}
preferenceValue={MINIMUM_PREFERENCE}
selected={
this.state.selectedPreference === MINIMUM_PREFERENCE
? true
: false
}
selected={this.state.selectedPreference === MINIMUM_PREFERENCE}
title={MINIMUM_NOTIFICATIONS_TITLE}
subtitle={MINIMUM_NOTIFICATIONS_SUBTITLE}
/>
......
......@@ -9,18 +9,14 @@ import Breadcrumb from 'components/common/Breadcrumb';
import Flag from 'components/common/Flag';
import ResourceList from 'components/common/ResourceList';
import TabsComponent from 'components/common/TabsComponent';
import {
mapDispatchToProps,
mapStateToProps,
ProfilePage,
ProfilePageProps,
RouteProps,
} from './';
import globalState from 'fixtures/globalState';
import { getMockRouterProps } from 'fixtures/mockRouter';
import { ResourceType } from 'interfaces/Resources';
import * as LogUtils from 'utils/logUtils';
import { indexDashboardsEnabled } from 'config/config-utils';
import {
AVATAR_SIZE,
BOOKMARKED_LABEL,
......@@ -30,16 +26,19 @@ import {
READ_LABEL,
READ_SOURCE,
} from './constants';
import {
mapDispatchToProps,
mapStateToProps,
ProfilePage,
ProfilePageProps,
RouteProps,
} from '.';
jest.mock('config/config-utils', () => ({
getDisplayNameByResource: jest.fn(() => 'Resource'),
indexDashboardsEnabled: jest.fn(),
}));
import * as LogUtils from 'utils/logUtils';
import { indexDashboardsEnabled } from 'config/config-utils';
describe('ProfilePage', () => {
const setup = (propOverrides?: Partial<ProfilePageProps>) => {
const routerProps = getMockRouterProps<RouteProps>(
......@@ -196,7 +195,7 @@ describe('ProfilePage', () => {
describe('generateTabKey', () => {
it('returns string used for the tab keys', () => {
const wrapper = setup().wrapper;
const { wrapper } = setup();
const givenResource = ResourceType.table;
expect(wrapper.instance().generateTabKey(givenResource)).toEqual(
`tab:${givenResource}`
......@@ -206,7 +205,7 @@ describe('ProfilePage', () => {
describe('generateTabTitle', () => {
it('returns string for tab title according to UI designs', () => {
const wrapper = setup().wrapper;
const { wrapper } = setup();
const givenResource = ResourceType.table;
expect(wrapper.instance().generateTabTitle(givenResource)).toEqual(
'Resource (4)'
......@@ -335,9 +334,9 @@ describe('ProfilePage', () => {
...globalState.user.profile.user,
display_name: '',
};
const wrapper = setup({
const { wrapper } = setup({
user: userCopy,
}).wrapper;
});
expect(wrapper.find('#profile-avatar').children().exists()).toBeFalsy();
});
......@@ -352,9 +351,9 @@ describe('ProfilePage', () => {
...globalState.user.profile.user,
is_active: false,
};
const wrapper = setup({
const { wrapper } = setup({
user: userCopy,
}).wrapper;
});
expect(
wrapper.find('.header-title-text').find(Flag).props()
).toMatchObject({
......
......@@ -93,7 +93,7 @@ export class ProfilePage extends React.Component<
}
componentDidUpdate() {
const userId = this.props.match.params.userId;
const { userId } = this.props.match.params;
if (userId !== this.state.userId) {
this.setState({ userId });
this.loadUserInfo(userId);
......@@ -184,10 +184,12 @@ export class ProfilePage extends React.Component<
return tabInfo;
};
/* TODO: Add support to direct to 404 page for edgecase of someone typing in
or pasting in a bad url. This would be consistent with TableDetail page behavior */
/*
TODO: Add support to direct to 404 page for edgecase of someone typing in
or pasting in a bad url. This would be consistent with TableDetail page behavior
*/
render() {
const user = this.props.user;
const { user } = this.props;
return (
<DocumentTitle title={`${user.display_name} - Amundsen Profile`}>
<div className="resource-detail-layout profile-page">
......@@ -196,11 +198,7 @@ export class ProfilePage extends React.Component<
<Breadcrumb />
<div id="profile-avatar" className="profile-avatar">
{user.display_name && user.display_name.length > 0 && (
<Avatar
name={user.display_name}
size={AVATAR_SIZE}
round={true}
/>
<Avatar name={user.display_name} size={AVATAR_SIZE} round />
)}
</div>
</div>
......@@ -227,20 +225,21 @@ export class ProfilePage extends React.Component<
</div>
</div>
<div className="header-section header-links">
{/*{*/}
{/* { */}
{/* // TODO - Implement deep links to open Slack *!/*/}
{/* user.is_active && user.slack_id &&*/}
{/* <a id="slack-link" href={user.slack_id} className='btn btn-flat-icon header-link' target='_blank'>*/}
{/* <img className='icon icon-dark icon-slack'/>*/}
{/* <span className="body-2">Slack</span>*/}
{/* </a>*/}
{/*}*/}
{/* } */}
{user.is_active && (
<a
id="email-link"
href={`mailto:${user.email}`}
className="btn btn-flat-icon header-link"
target="_blank"
rel="noreferrer"
>
<img className="icon icon-dark icon-mail" />
<span className="email-link-label body-2">{user.email}</span>
......@@ -252,6 +251,7 @@ export class ProfilePage extends React.Component<
href={user.profile_url}
className="btn btn-flat-icon header-link"
target="_blank"
rel="noreferrer"
>
<span className="icon icon-dark icon-users" />
<span className="profile-link-label body-2">
......@@ -265,6 +265,7 @@ export class ProfilePage extends React.Component<
href={`https://github.com/${user.github_username}`}
className="btn btn-flat-icon header-link"
target="_blank"
rel="noreferrer"
>
<img className="icon icon-dark icon-github" />
<span className="github-link-label body-2">Github</span>
......
......@@ -10,7 +10,6 @@
}
.profile-body {
// TODO: consider moving logic for empty content into Tab component
.empty-tab-message {
margin-top: 32px;
......
......@@ -2,26 +2,25 @@ import * as React from 'react';
import { mocked } from 'ts-jest/utils';
import { shallow } from 'enzyme';
import globalState from 'fixtures/globalState';
import { ResourceType } from 'interfaces/Resources';
import {
getDisplayNameByResource,
indexDashboardsEnabled,
indexUsersEnabled,
} from 'config/config-utils';
import {
mapDispatchToProps,
mapStateToProps,
ResourceSelector,
ResourceSelectorProps,
} from '../';
import globalState from 'fixtures/globalState';
import { ResourceType } from 'interfaces/Resources';
} from '..';
jest.mock('config/config-utils', () => ({
getDisplayNameByResource: jest.fn(() => 'Resource'),
indexUsersEnabled: jest.fn(),
indexDashboardsEnabled: jest.fn(),
}));
import {
getDisplayNameByResource,
indexDashboardsEnabled,
indexUsersEnabled,
} from 'config/config-utils';
describe('ResourceSelector', () => {
const setup = (propOverrides?: Partial<ResourceSelectorProps>) => {
......
......@@ -55,8 +55,8 @@ export class CheckBoxFilter extends React.Component<CheckBoxFilterProps> {
};
onCheckboxChange = (e: React.ChangeEvent<HTMLInputElement>) => {
let checkedValues = this.props.checkedValues;
const value = e.target.value;
let { checkedValues } = this.props;
const { value } = e.target;
const categoryId = e.target.name;
if (e.target.checked) {
......
import * as React from 'react';
import { shallow } from 'enzyme';
import {
CheckBoxFilter,
CheckBoxFilterProps,
mapDispatchToProps,
mapStateToProps,
} from '../';
import CheckBoxItem from 'components/common/Inputs/CheckBoxItem';
import globalState from 'fixtures/globalState';
......@@ -14,6 +8,12 @@ import globalState from 'fixtures/globalState';
import { GlobalState } from 'ducks/rootReducer';
import { FilterType, ResourceType } from 'interfaces';
import {
CheckBoxFilter,
CheckBoxFilterProps,
mapDispatchToProps,
mapStateToProps,
} from '..';
describe('CheckBoxFilter', () => {
const setup = (propOverrides?: Partial<CheckBoxFilterProps>) => {
......@@ -142,7 +142,7 @@ describe('CheckBoxFilter', () => {
describe('mapStateToProps', () => {
const mockCategoryId = 'database';
const props = setup({ categoryId: mockCategoryId }).props;
const { props } = setup({ categoryId: mockCategoryId });
const mockFilters = {
hive: true,
};
......@@ -186,7 +186,7 @@ describe('CheckBoxFilter', () => {
});
it('sets checkedValues to empty object if no filters exist for the given category', () => {
const props = setup({ categoryId: 'fakeCategory' }).props;
const { props } = setup({ categoryId: 'fakeCategory' });
result = mapStateToProps(mockStateWithFilters, props);
expect(result.checkedValues).toEqual({});
});
......@@ -196,7 +196,7 @@ describe('CheckBoxFilter', () => {
let dispatch;
let result;
beforeAll(() => {
const props = setup().props;
const { props } = setup();
dispatch = jest.fn(() => Promise.resolve());
result = mapDispatchToProps(dispatch);
});
......
......@@ -7,17 +7,14 @@ import {
UpdateFilterRequest,
} from 'ducks/search/filters/reducer';
import { CLEAR_BTN_TEXT } from '../constants';
import { GlobalState } from 'ducks/rootReducer';
import { FilterType } from 'interfaces';
import InfoButton from 'components/common/InfoButton';
import { CLEAR_BTN_TEXT } from '../constants';
import CheckBoxFilter, { CheckboxFilterProperties } from '../CheckBoxFilter';
import InputFilter from '../InputFilter';
import { FilterType } from 'interfaces';
import InfoButton from 'components/common/InfoButton';
export interface OwnProps {
categoryId: string;
helpText?: string;
......@@ -69,6 +66,7 @@ export class FilterSection extends React.Component<FilterSectionProps> {
)}
</div>
{hasValue && (
/* eslint-disable jsx-a11y/anchor-is-valid */
<a onClick={this.onClearFilter} className="btn btn-flat-icon">
<img className="icon icon-left" />
<span>{CLEAR_BTN_TEXT}</span>
......
......@@ -3,18 +3,17 @@ import { shallow } from 'enzyme';
import { GlobalState } from 'ducks/rootReducer';
import {
FilterSection,
FilterSectionProps,
mapDispatchToProps,
mapStateToProps,
} from '../';
import globalState from 'fixtures/globalState';
import { FilterType, ResourceType } from 'interfaces';
import InfoButton from 'components/common/InfoButton';
import {
FilterSection,
FilterSectionProps,
mapDispatchToProps,
mapStateToProps,
} from '..';
import { CLEAR_BTN_TEXT } from '../../constants';
describe('FilterSection', () => {
......@@ -93,7 +92,7 @@ describe('FilterSection', () => {
it('renders InfoButton with correct props if props.helpText exists', () => {
const mockHelpText = 'Help me';
const wrapper = setup({ helpText: mockHelpText }).wrapper;
const { wrapper } = setup({ helpText: mockHelpText });
const infoButton = wrapper.find(InfoButton);
expect(infoButton.exists()).toBe(true);
expect(infoButton.props().infoText).toBe(mockHelpText);
......@@ -143,19 +142,19 @@ describe('FilterSection', () => {
let result;
describe('sets hasValue as true', () => {
it('when CHECKBOX_SELECT filter has value', () => {
const props = setup({
const { props } = setup({
categoryId: 'database',
type: FilterType.CHECKBOX_SELECT,
}).props;
});
result = mapStateToProps(mockStateWithFilters, props);
expect(result.hasValue).toBe(true);
});
it('when INPUT_SELECT filter has value', () => {
const props = setup({
const { props } = setup({
categoryId: 'schema',
type: FilterType.INPUT_SELECT,
}).props;
});
result = mapStateToProps(mockStateWithFilters, props);
expect(result.hasValue).toBe(true);
});
......@@ -163,25 +162,25 @@ describe('FilterSection', () => {
describe('sets hasValue as false', () => {
it('when CHECKBOX_SELECT filter has no value', () => {
const props = setup({
const { props } = setup({
categoryId: 'database',
type: FilterType.CHECKBOX_SELECT,
}).props;
});
result = mapStateToProps(mockStateWithOutFilters, props);
expect(result.hasValue).toBe(false);
});
it('when INPUT_SELECT filter has no value', () => {
const props = setup({
const { props } = setup({
categoryId: 'schema',
type: FilterType.INPUT_SELECT,
}).props;
});
result = mapStateToProps(mockStateWithOutFilters, props);
expect(result.hasValue).toBe(false);
});
it('when no filters exist for the given category', () => {
const props = setup({ categoryId: 'fakeCategory' }).props;
const { props } = setup({ categoryId: 'fakeCategory' });
result = mapStateToProps(mockStateWithFilters, props);
expect(result.hasValue).toEqual(false);
});
......@@ -192,7 +191,7 @@ describe('FilterSection', () => {
let dispatch;
let result;
beforeAll(() => {
const props = setup().props;
const { props } = setup();
dispatch = jest.fn(() => Promise.resolve());
result = mapDispatchToProps(dispatch);
});
......
......@@ -7,9 +7,8 @@ import {
UpdateFilterRequest,
} from 'ducks/search/filters/reducer';
import { APPLY_BTN_TEXT } from '../constants';
import { GlobalState } from 'ducks/rootReducer';
import { APPLY_BTN_TEXT } from '../constants';
interface OwnProps {
categoryId: string;
......@@ -53,7 +52,7 @@ export class InputFilter extends React.Component<
onApplyChanges = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!!this.state.value) {
if (this.state.value) {
this.props.updateFilter(this.props.categoryId, this.state.value);
} else {
this.props.updateFilter(this.props.categoryId, undefined);
......
import * as React from 'react';
import { shallow } from 'enzyme';
import {
InputFilter,
InputFilterProps,
mapDispatchToProps,
mapStateToProps,
} from '../';
import { APPLY_BTN_TEXT } from '../../constants';
import { GlobalState } from 'ducks/rootReducer';
import globalState from 'fixtures/globalState';
import { FilterType, ResourceType } from 'interfaces';
import { APPLY_BTN_TEXT } from '../../constants';
import {
InputFilter,
InputFilterProps,
mapDispatchToProps,
mapStateToProps,
} from '..';
describe('InputFilter', () => {
const setStateSpy = jest.spyOn(InputFilter.prototype, 'setState');
......@@ -156,7 +154,7 @@ describe('InputFilter', () => {
describe('mapStateToProps', () => {
const mockCategoryId = 'schema';
const props = setup({ categoryId: mockCategoryId }).props;
const { props } = setup({ categoryId: mockCategoryId });
const mockFilters = 'schema_name';
const mockStateWithFilters: GlobalState = {
......@@ -198,7 +196,7 @@ describe('InputFilter', () => {
});
it('sets value to empty string if no filters exist for the given category', () => {
const props = setup({ categoryId: 'fakeCategory' }).props;
const { props } = setup({ categoryId: 'fakeCategory' });
result = mapStateToProps(mockStateWithFilters, props);
expect(result.value).toEqual('');
});
......@@ -208,7 +206,7 @@ describe('InputFilter', () => {
let dispatch;
let result;
beforeAll(() => {
const props = setup().props;
const { props } = setup();
dispatch = jest.fn(() => Promise.resolve());
result = mapDispatchToProps(dispatch);
});
......
......@@ -3,12 +3,10 @@ import { connect } from 'react-redux';
import { GlobalState } from 'ducks/rootReducer';
import { CheckboxFilterProperties } from './CheckBoxFilter';
import FilterSection from './FilterSection';
import { getFilterConfigByResource } from 'config/config-utils';
import { FilterType, ResourceType } from 'interfaces';
import { CheckboxFilterProperties } from './CheckBoxFilter';
import FilterSection from './FilterSection';
import './styles.scss';
......
......@@ -22,7 +22,7 @@
display: flex;
button,
input[type=text] {
input[type='text'] {
margin-top: $spacer-1;
}
......
......@@ -15,7 +15,7 @@ import {
SearchFilterProps,
FilterSection,
CheckboxFilterSection,
} from '../';
} from '..';
describe('SearchFilter', () => {
const setup = (propOverrides?: Partial<SearchFilterProps>) => {
......
......@@ -2,7 +2,11 @@ import * as React from 'react';
import './styles.scss';
const SearchPanel: React.SFC = ({ children }) => {
type SearchPanelProps = {
children: React.ReactNode;
};
const SearchPanel: React.SFC = ({ children }: SearchPanelProps) => {
return (
<div className="search-control-panel">
{React.Children.map(children, (child, index) => {
......
import * as React from 'react';
import { shallow } from 'enzyme';
import SearchPanel from '../';
import SearchPanel from '..';
describe('SearchPanel', () => {
const resourceChild = <div>I am a resource selector</div>;
......
......@@ -5,12 +5,19 @@ import * as History from 'history';
import { shallow } from 'enzyme';
import { ResourceType } from 'interfaces';
import LoadingSpinner from 'components/common/LoadingSpinner';
import ResourceSelector from 'components/SearchPage/ResourceSelector';
import SearchFilter from 'components/SearchPage/SearchFilter';
import SearchPanel from 'components/SearchPage/SearchPanel';
import PaginatedApiResourceList from 'components/common/ResourceList/PaginatedApiResourceList';
import globalState from 'fixtures/globalState';
import {
mapDispatchToProps,
mapStateToProps,
SearchPage,
SearchPageProps,
} from '.';
defaultEmptyFilters,
datasetFilterExample,
} from 'fixtures/search/filters';
import { getMockRouterProps } from 'fixtures/mockRouter';
import {
DOCUMENT_TITLE_SUFFIX,
PAGE_INDEX_ERROR_MESSAGE,
......@@ -23,19 +30,12 @@ import {
TABLE_RESOURCE_TITLE,
USER_RESOURCE_TITLE,
} from './constants';
import LoadingSpinner from 'components/common/LoadingSpinner';
import ResourceSelector from 'components/SearchPage/ResourceSelector';
import SearchFilter from 'components/SearchPage/SearchFilter';
import SearchPanel from 'components/SearchPage/SearchPanel';
import PaginatedApiResourceList from 'components/common/ResourceList/PaginatedApiResourceList';
import globalState from 'fixtures/globalState';
import {
defaultEmptyFilters,
datasetFilterExample,
} from 'fixtures/search/filters';
import { getMockRouterProps } from 'fixtures/mockRouter';
mapDispatchToProps,
mapStateToProps,
SearchPage,
SearchPageProps,
} from '.';
describe('SearchPage', () => {
const setStateSpy = jest.spyOn(SearchPage.prototype, 'setState');
......
......@@ -7,9 +7,6 @@ import { Search as UrlSearch } from 'history';
import LoadingSpinner from 'components/common/LoadingSpinner';
import PaginatedApiResourceList from 'components/common/ResourceList/PaginatedApiResourceList';
import ResourceSelector from './ResourceSelector';
import SearchFilter from './SearchFilter';
import SearchPanel from './SearchPanel';
import { GlobalState } from 'ducks/rootReducer';
import { submitSearchResource, urlDidUpdate } from 'ducks/search/reducer';
......@@ -23,6 +20,9 @@ import {
} from 'ducks/search/types';
import { Resource, ResourceType, SearchType } from 'interfaces';
import SearchPanel from './SearchPanel';
import SearchFilter from './SearchFilter';
import ResourceSelector from './ResourceSelector';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
......
import * as React from 'react';
import ColumnListItem from '../ColumnListItem';
import { TableColumn } from 'interfaces';
import ColumnListItem from '../ColumnListItem';
import './styles.scss';
......@@ -9,8 +8,9 @@ interface ColumnListProps {
columns?: TableColumn[];
}
// TODO - convert into a component for easier testing
const ColumnList: React.SFC<ColumnListProps> = ({ columns }) => {
const ColumnList: React.SFC<ColumnListProps> = ({
columns,
}: ColumnListProps) => {
if (columns.length < 1) {
return <div />;
// ToDo: return No Results Message
......
......@@ -62,7 +62,7 @@ describe('ColumnListItem', () => {
it('turns expanded state to the opposite state', () => {
setStateSpy.mockClear();
const isExpanded = instance.state.isExpanded;
const { isExpanded } = instance.state;
instance.toggleExpand(null);
expect(setStateSpy).toHaveBeenCalledWith({ isExpanded: !isExpanded });
});
......
......@@ -84,7 +84,6 @@ export class ColumnListItem extends React.Component<
} else {
text = `${truncatedType}...`;
}
return;
}
});
......@@ -102,7 +101,7 @@ export class ColumnListItem extends React.Component<
trigger={['click']}
placement="left"
overlay={popoverHover}
rootClose={true}
rootClose
>
<a
className="column-type"
......@@ -146,10 +145,10 @@ export class ColumnListItem extends React.Component<
<Dropdown
id={`detail-list-item-dropdown:${this.props.index}`}
onClick={this.stopPropagation}
pullRight={true}
pullRight
className="column-dropdown"
>
<Dropdown.Toggle noCaret={true}>
<Dropdown.Toggle noCaret>
<img className="icon icon-more" />
</Dropdown.Toggle>
<Dropdown.Menu>
......
......@@ -78,7 +78,7 @@ export class DataPreviewButton extends React.Component<
}
componentDidMount() {
const tableData = this.props.tableData;
const { tableData } = this.props;
this.props.getPreviewData({
database: tableData.database,
schema: tableData.schema,
......@@ -108,7 +108,7 @@ export class DataPreviewButton extends React.Component<
}
renderModalBody() {
const previewData = this.props.previewData;
const { previewData } = this.props;
if (this.props.status === LoadingStatus.SUCCESS) {
if (
......@@ -157,7 +157,7 @@ export class DataPreviewButton extends React.Component<
}
renderPreviewButton() {
const previewData = this.props.previewData;
const { previewData } = this.props;
// Based on the state, the preview button will show different things.
let buttonText = 'Loading...';
......@@ -232,7 +232,7 @@ export class DataPreviewButton extends React.Component<
<>
{this.renderPreviewButton()}
<Modal show={this.state.showModal} onHide={this.handleClose}>
<Modal.Header className="text-center" closeButton={true}>
<Modal.Header className="text-center" closeButton>
<Modal.Title>{this.props.modalTitle}</Modal.Title>
</Modal.Header>
<Modal.Body>{this.renderModalBody()}</Modal.Body>
......
......@@ -23,6 +23,7 @@ class ExploreButton extends React.Component<ExploreButtonProps> {
target="_blank"
id="explore-sql"
onClick={logClick}
rel="noreferrer"
>
Explore
</a>
......
......@@ -16,7 +16,7 @@ export function renderReader(
index: number,
readers: TableReader[]
) {
const user = reader.user;
const { user } = reader;
let link = user.profile_url;
let target = '_blank';
if (AppConfig.indexUsers.enabled) {
......@@ -42,7 +42,7 @@ export function renderReader(
>
<Avatar
name={user.display_name}
round={true}
round
size={25}
style={{ zIndex: readers.length - index, position: 'relative' }}
/>
......@@ -51,7 +51,9 @@ export function renderReader(
);
}
const FrequentUsers: React.SFC<FrequentUsersProps> = ({ readers }) => {
const FrequentUsers: React.SFC<FrequentUsersProps> = ({
readers,
}: FrequentUsersProps) => {
if (readers.length === 0) {
return <label className="body-3">No frequent users exist</label>;
}
......
......@@ -9,7 +9,9 @@ export interface LineageLinkProps {
tableData: TableMetadata;
}
const LineageLink: React.SFC<LineageLinkProps> = ({ tableData }) => {
const LineageLink: React.SFC<LineageLinkProps> = ({
tableData,
}: LineageLinkProps) => {
const config = AppConfig.tableLineage;
if (!config.isEnabled) return null;
......@@ -26,6 +28,7 @@ const LineageLink: React.SFC<LineageLinkProps> = ({ tableData }) => {
target="_blank"
id="explore-lineage"
onClick={logClick}
rel="noreferrer"
>
<AvatarLabel label={label} src={config.iconPath} />
</a>
......
......@@ -126,7 +126,7 @@ export class OwnerEditor extends React.Component<
recordAddItem = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const value = this.inputRef.current.value;
const { value } = this.inputRef.current;
if (value) {
this.inputRef.current.value = '';
const newTempItemProps = {
......@@ -167,7 +167,7 @@ export class OwnerEditor extends React.Component<
<form className="component-form" onSubmit={this.recordAddItem}>
<input
id="add-item-input"
autoFocus={true}
autoFocus
placeholder={`Please enter ${
AppConfig.userIdLabel || Constants.USERID_LABEL
}`}
......@@ -236,6 +236,7 @@ export class OwnerEditor extends React.Component<
target="_blank"
id={`table-owners:${key}`}
onClick={logClick}
rel="noreferrer"
>
{avatarLabel}
</a>
......
......@@ -4,6 +4,7 @@ import { shallow } from 'enzyme';
import AppConfig from 'config/config';
import globalState from 'fixtures/globalState';
import { NotificationType } from 'interfaces';
import {
ComponentProps,
ReportTableIssue,
......@@ -11,7 +12,6 @@ import {
mapDispatchToProps,
mapStateToProps,
} from '.';
import { NotificationType } from 'interfaces';
const globalAny: any = global;
......
......@@ -6,7 +6,6 @@ import { GlobalState } from 'ducks/rootReducer';
import { createIssue } from 'ducks/issue/reducer';
import { CreateIssueRequest } from 'ducks/issue/types';
import './styles.scss';
import { REPORT_DATA_ISSUE_TEXT, TABLE_OWNERS_NOTE } from './constants';
import { logClick } from 'ducks/utilMethods';
import {
TableMetadata,
......@@ -14,6 +13,7 @@ import {
NotificationPayload,
NotificationType,
} from 'interfaces';
import { REPORT_DATA_ISSUE_TEXT, TABLE_OWNERS_NOTE } from './constants';
export interface ComponentProps {
tableKey: string;
......@@ -103,6 +103,7 @@ export class ReportTableIssue extends React.Component<
render() {
return (
<>
{/* eslint-disable jsx-a11y/anchor-is-valid */}
<a
href="javascript:void(0)"
className="report-table-issue-link"
......@@ -116,7 +117,7 @@ export class ReportTableIssue extends React.Component<
<button
type="button"
className="btn btn-close"
aria-label={'close'}
aria-label="close"
onClick={this.toggle}
/>
<form id="report-table-issue-form" onSubmit={this.submitForm}>
......@@ -125,7 +126,7 @@ export class ReportTableIssue extends React.Component<
<input
name="title"
className="form-control"
required={true}
required
maxLength={200}
/>
</div>
......@@ -135,7 +136,7 @@ export class ReportTableIssue extends React.Component<
name="description"
className="form-control"
rows={5}
required={true}
required
maxLength={2000}
/>
</div>
......
......@@ -2,14 +2,14 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import globalState from 'fixtures/globalState';
import { RequestMetadataType } from 'interfaces';
import {
RequestDescriptionText,
mapDispatchToProps,
RequestDescriptionTextProps,
} from '.';
import globalState from 'fixtures/globalState';
import { REQUEST_DESCRIPTION } from './constants';
import { RequestMetadataType } from 'interfaces';
describe('RequestDescriptionText', () => {
const setup = (propOverrides?: Partial<RequestDescriptionTextProps>) => {
......
......@@ -6,9 +6,8 @@ import { connect } from 'react-redux';
import { OpenRequestAction } from 'ducks/notification/types';
import { openRequestDescriptionDialog } from 'ducks/notification/reducer';
import { bindActionCreators } from 'redux';
import { REQUEST_DESCRIPTION } from './constants';
import { RequestMetadataType } from 'interfaces';
import { REQUEST_DESCRIPTION } from './constants';
export interface DispatchFromProps {
openRequestDescriptionDialog: (
......
......@@ -95,25 +95,25 @@ describe('RequestMetadataForm', () => {
describe('getFlashMessageString', () => {
it('returns SEND_SUCCESS_MESSAGE if SendingState.COMPLETE', () => {
const wrapper = setup({ sendState: SendingState.COMPLETE }).wrapper;
const { wrapper } = setup({ sendState: SendingState.COMPLETE });
expect(wrapper.instance().getFlashMessageString()).toEqual(
SEND_SUCCESS_MESSAGE
);
});
it('returns SEND_FAILURE_MESSAGE if SendingState.ERROR', () => {
const wrapper = setup({ sendState: SendingState.ERROR }).wrapper;
const { wrapper } = setup({ sendState: SendingState.ERROR });
expect(wrapper.instance().getFlashMessageString()).toEqual(
SEND_FAILURE_MESSAGE
);
});
it('returns SEND_INPROGRESS_MESSAGE if SendingState.WAITING', () => {
const wrapper = setup({ sendState: SendingState.WAITING }).wrapper;
const { wrapper } = setup({ sendState: SendingState.WAITING });
expect(wrapper.instance().getFlashMessageString()).toEqual(
SEND_INPROGRESS_MESSAGE
);
});
it('returns empty striong if sending state not handled', () => {
const wrapper = setup({ sendState: SendingState.IDLE }).wrapper;
const { wrapper } = setup({ sendState: SendingState.IDLE });
expect(wrapper.instance().getFlashMessageString()).toEqual('');
});
});
......@@ -150,11 +150,11 @@ describe('RequestMetadataForm', () => {
wrapper.instance().submitNotification({ preventDefault: jest.fn() });
expect(submitNotificationSpy).toHaveBeenCalledWith(
mockFormData['recipients'].split(','),
mockFormData['sender'],
mockFormData.recipients.split(','),
mockFormData.sender,
NotificationType.METADATA_REQUESTED,
{
comment: mockFormData['comment'],
comment: mockFormData.comment,
resource_name: `${schema}.${name}`,
resource_path: `/table_detail/${cluster}/${database}/${schema}/${name}`,
description_requested: true,
......
......@@ -16,6 +16,15 @@ import FlashMessage from 'components/common/FlashMessage';
import { GlobalState } from 'ducks/rootReducer';
import {
CloseRequestAction,
OpenRequestAction,
SubmitNotificationRequest,
} from 'ducks/notification/types';
import {
closeRequestDescriptionDialog,
submitNotification,
} from 'ducks/notification/reducer';
import {
TITLE_TEXT,
FROM_LABEL,
......@@ -33,15 +42,6 @@ import {
SEND_INPROGRESS_MESSAGE,
SEND_SUCCESS_MESSAGE,
} from './constants';
import {
CloseRequestAction,
OpenRequestAction,
SubmitNotificationRequest,
} from 'ducks/notification/types';
import {
closeRequestDescriptionDialog,
submitNotification,
} from 'ducks/notification/reducer';
interface StateFromProps {
columnName?: string;
......@@ -162,7 +162,7 @@ export class RequestMetadataForm extends React.Component<
<button
type="button"
className="btn btn-close"
aria-label={'Close'}
aria-label="Close"
onClick={this.closeDialog}
/>
</div>
......@@ -174,9 +174,9 @@ export class RequestMetadataForm extends React.Component<
autoComplete="off"
name="sender"
className="form-control"
required={true}
required
value={userEmail}
readOnly={true}
readOnly
/>
</div>
<div id="recipients-form-group" className="form-group">
......@@ -186,8 +186,8 @@ export class RequestMetadataForm extends React.Component<
autoComplete="off"
name="recipients"
className="form-control"
required={true}
multiple={true}
required
multiple
defaultValue={tableOwners.join(RECIPIENT_LIST_DELIMETER)}
/>
</div>
......
......@@ -8,7 +8,9 @@ export interface SourceLinkProps {
tableSource: TableSource;
}
const SourceLink: React.SFC<SourceLinkProps> = ({ tableSource }) => {
const SourceLink: React.SFC<SourceLinkProps> = ({
tableSource,
}: SourceLinkProps) => {
if (tableSource === null || tableSource.source === null) return null;
const image =
......@@ -20,6 +22,7 @@ const SourceLink: React.SFC<SourceLinkProps> = ({ tableSource }) => {
id="explore-source"
onClick={logClick}
target="_blank"
rel="noreferrer"
>
<AvatarLabel label={tableSource.source_type} src={image} />
</a>
......
......@@ -3,9 +3,12 @@ import * as React from 'react';
import { mocked } from 'ts-jest/utils';
import { shallow } from 'enzyme';
import TableHeaderBullets, { TableHeaderBulletsProps } from '.';
import { ResourceType } from 'interfaces/Resources';
import {
getSourceDisplayName,
getDisplayNameByResource,
} from 'config/config-utils';
import TableHeaderBullets, { TableHeaderBulletsProps } from '.';
const MOCK_RESOURCE_DISPLAY_NAME = 'Test';
const MOCK_DB_DISPLAY_NAME = 'AlsoTest';
......@@ -14,10 +17,6 @@ jest.mock('config/config-utils', () => ({
getDisplayNameByResource: jest.fn(),
getSourceDisplayName: jest.fn(),
}));
import {
getSourceDisplayName,
getDisplayNameByResource,
} from 'config/config-utils';
describe('TableHeaderBullets', () => {
const setup = (propOverrides?: Partial<TableHeaderBulletsProps>) => {
......
......@@ -15,7 +15,7 @@ export interface TableHeaderBulletsProps {
const TableHeaderBullets: React.SFC<TableHeaderBulletsProps> = ({
cluster,
database,
}) => {
}: TableHeaderBulletsProps) => {
return (
<ul className="header-bullets">
<li>{getDisplayNameByResource(ResourceType.table)}</li>
......
......@@ -5,6 +5,8 @@ import { shallow } from 'enzyme';
import AppConfig from 'config/config';
import globalState from 'fixtures/globalState';
import { NO_DATA_ISSUES_TEXT } from 'components/TableDetail/TableIssues/constants';
import ReportTableIssue from 'components/TableDetail/ReportTableIssue';
import {
TableIssues,
TableIssueProps,
......@@ -12,9 +14,6 @@ import {
mapDispatchToProps,
} from '.';
import { NO_DATA_ISSUES_TEXT } from 'components/TableDetail/TableIssues/constants';
import ReportTableIssue from 'components/TableDetail/ReportTableIssue';
describe('TableIssues', () => {
const setStateSpy = jest.spyOn(TableIssues.prototype, 'setState');
......
......@@ -49,6 +49,7 @@ export class TableIssues extends React.Component<TableIssueProps> {
target="_blank"
href={issue.url}
onClick={logClick}
rel="noreferrer"
>
<span>{issue.issue_key}</span>
</a>
......@@ -91,6 +92,7 @@ export class TableIssues extends React.Component<TableIssueProps> {
id="more-issues-link"
className="table-issue-more-issues"
target="_blank"
rel="noreferrer"
href={this.props.allIssuesUrl}
onClick={logClick}
>
......
import * as React from 'react';
import { shallow, mount } from 'enzyme';
import WatermarkLabel, { WatermarkLabelProps } from '.';
import {
NO_WATERMARK_LINE_1,
NO_WATERMARK_LINE_2,
WatermarkType,
} from 'components/TableDetail/WatermarkLabel/constants';
import WatermarkLabel, { WatermarkLabelProps } from '.';
describe('WatermarkLabel', () => {
const setup = (propOverrides?: Partial<WatermarkLabelProps>) => {
......
......@@ -2,6 +2,7 @@ import * as React from 'react';
import './styles.scss';
import { Watermark } from 'interfaces';
import { formatDate } from 'utils/dateUtils';
import {
HIGH_WATERMARK_LABEL,
NO_WATERMARK_LINE_1,
......@@ -10,7 +11,6 @@ import {
WATERMARK_INPUT_FORMAT,
WatermarkType,
} from './constants';
import { formatDate } from 'utils/dateUtils';
export interface WatermarkLabelProps {
watermarks: Watermark[];
......
......@@ -9,7 +9,9 @@ export interface WriterLinkProps {
tableWriter: TableWriter;
}
const WriterLink: React.SFC<WriterLinkProps> = ({ tableWriter }) => {
const WriterLink: React.SFC<WriterLinkProps> = ({
tableWriter,
}: WriterLinkProps) => {
if (tableWriter === null || tableWriter.application_url === null) {
return null;
}
......@@ -31,6 +33,7 @@ const WriterLink: React.SFC<WriterLinkProps> = ({ tableWriter }) => {
href={tableWriter.application_url}
onClick={logClick}
target="_blank"
rel="noreferrer"
>
<AvatarLabel label={tableWriter.name} src={image} />
</a>
......
......@@ -7,7 +7,7 @@ import { tableMetadata } from '../../fixtures/metadata/table';
import LoadingSpinner from '../common/LoadingSpinner';
import { TableDetail, TableDetailProps, MatchProps } from './';
import { TableDetail, TableDetailProps, MatchProps } from '.';
const setup = (
propOverrides?: Partial<TableDetailProps>,
......
......@@ -90,6 +90,7 @@ export class TableDetail extends React.Component<
TableDetailProps & RouteComponentProps<any>
> {
private key: string;
private didComponentMount: boolean = false;
componentDidMount() {
......@@ -111,7 +112,7 @@ export class TableDetail extends React.Component<
}
getDisplayName() {
const params = this.props.match.params;
const { params } = this.props.match;
return `${params.schema}.${params.table}`;
}
......@@ -122,7 +123,7 @@ export class TableDetail extends React.Component<
we can't pass it as a single URL parameter without encodeURIComponent which makes ugly URLs.
DO NOT CHANGE
*/
const params = this.props.match.params;
const { params } = this.props.match;
return `${params.database}://${params.cluster}.${params.schema}/${params.table}`;
}
......@@ -150,7 +151,7 @@ export class TableDetail extends React.Component<
title: `Dashboards (${this.props.tableData.dashboards.length})`,
});
return <TabsComponent tabs={tabInfo} defaultTab={'columns'} />;
return <TabsComponent tabs={tabInfo} defaultTab="columns" />;
}
render() {
......@@ -272,7 +273,7 @@ export class TableDetail extends React.Component<
)}
{data.programmatic_descriptions.map((d) => (
<section key={d.source} className="column-layout-2">
<EditableSection title={d.source} readOnly={true}>
<EditableSection title={d.source} readOnly>
<EditableText
maxLength={999999}
value={d.text}
......
import * as React from 'react';
import { shallow } from 'enzyme';
import { mapDispatchToProps, TagInfo, TagInfoProps } from '.';
import * as UtilMethods from 'ducks/utilMethods';
import { mapDispatchToProps, TagInfo, TagInfoProps } from '.';
const logClickSpy = jest.spyOn(UtilMethods, 'logClick');
logClickSpy.mockImplementation(() => null);
......
......@@ -9,9 +9,9 @@ import { GlobalState } from 'ducks/rootReducer';
import { getAllTags, updateTags } from 'ducks/tags/reducer';
import { GetAllTagsRequest, UpdateTagsRequest } from 'ducks/tags/types';
import TagInfo from '../TagInfo';
import { EditableSectionChildProps } from 'components/common/EditableSection';
import { ResourceType, Tag, UpdateMethod, UpdateTagData } from 'interfaces';
import TagInfo from '../TagInfo';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
......@@ -150,7 +150,7 @@ class TagInput extends React.Component<TagInputProps, TagInputState> {
tag =
actionType === 'select-option'
? actionPayload.option.value
: currentTags[currentTags.length - 1]['value'];
: currentTags[currentTags.length - 1].value;
if (tag === BATCH_EDIT_TAG_OPTION) {
currentTags.pop();
this.handleShow();
......@@ -287,14 +287,14 @@ class TagInput extends React.Component<TagInputProps, TagInputState> {
} else {
tagBody = (
<CreatableSelect
autoFocus={true}
autoFocus
className="basic-multi-select"
classNamePrefix="amundsen"
components={componentOverides}
isClearable={false}
isDisabled={this.props.isLoading}
isLoading={this.props.isLoading}
isMulti={true}
isMulti
isValidNewOption={this.isValidNewOption}
name="tags"
noOptionsMessage={this.noOptionsMessage}
......
......@@ -16,16 +16,10 @@ const AvatarLabel: React.SFC<AvatarLabelProps> = ({
labelClass,
label,
src,
}) => {
}: AvatarLabelProps) => {
return (
<div className="avatar-label-component">
<Avatar
className={avatarClass}
name={label}
src={src}
size={24}
round={true}
/>
<Avatar className={avatarClass} name={label} src={src} size={24} round />
<span className={`avatar-label body-2 ${labelClass}`}>{label}</span>
</div>
);
......
import * as React from 'react';
import { shallow } from 'enzyme';
import BadgeList from '.';
import Flag from 'components/common/Flag';
import { BadgeStyle } from 'config/config-types';
import * as ConfigUtils from 'config/config-utils';
import { Badge, TagType } from 'interfaces/Tags';
import BadgeList from '.';
describe('BadgeList', () => {
const getBadgeConfigSpy = jest.spyOn(ConfigUtils, 'getBadgeConfig');
......
......@@ -8,7 +8,7 @@ export interface BadgeListProps {
badges: Badge[];
}
const BadgeList: React.SFC<BadgeListProps> = ({ badges }) => {
const BadgeList: React.SFC<BadgeListProps> = ({ badges }: BadgeListProps) => {
return (
<span className="badge-list">
{badges.map((badge, index) => {
......
......@@ -5,21 +5,21 @@ import { mocked } from 'ts-jest/utils';
import globalState from 'fixtures/globalState';
import { ResourceType } from 'interfaces';
import { MyBookmarks, MyBookmarksProps, mapStateToProps } from './';
import PaginatedResourceList from 'components/common/ResourceList/PaginatedResourceList';
import TabsComponent from 'components/common/TabsComponent';
import { indexDashboardsEnabled } from 'config/config-utils';
import {
BOOKMARK_TITLE,
BOOKMARKS_PER_PAGE,
EMPTY_BOOKMARK_MESSAGE,
MY_BOOKMARKS_SOURCE_NAME,
} from './constants';
import { MyBookmarks, MyBookmarksProps, mapStateToProps } from '.';
jest.mock('config/config-utils', () => ({
getDisplayNameByResource: jest.fn(() => 'Resource'),
indexDashboardsEnabled: jest.fn(),
}));
import { indexDashboardsEnabled } from 'config/config-utils';
describe('MyBookmarks', () => {
const setStateSpy = jest.spyOn(MyBookmarks.prototype, 'setState');
......@@ -123,7 +123,7 @@ describe('MyBookmarks', () => {
describe('generateTabKey', () => {
it('returns string used for the tab keys', () => {
const wrapper = setup().wrapper;
const { wrapper } = setup();
const givenResource = ResourceType.table;
expect(wrapper.instance().generateTabKey(givenResource)).toEqual(
`bookmarktab:${givenResource}`
......
......@@ -8,14 +8,14 @@ import {
getDisplayNameByResource,
indexDashboardsEnabled,
} from 'config/config-utils';
import PaginatedResourceList from 'components/common/ResourceList/PaginatedResourceList';
import TabsComponent from 'components/common/TabsComponent';
import {
BOOKMARK_TITLE,
BOOKMARKS_PER_PAGE,
EMPTY_BOOKMARK_MESSAGE,
MY_BOOKMARKS_SOURCE_NAME,
} from './constants';
import PaginatedResourceList from 'components/common/ResourceList/PaginatedResourceList';
import TabsComponent from 'components/common/TabsComponent';
interface StateFromProps {
myBookmarks: ResourceDict<Bookmark[]>;
......
......@@ -21,7 +21,9 @@ type BreadcrumbDirection = 'left' | 'right';
export type BreadcrumbProps = OwnProps & MapDispatchToProps;
export const Breadcrumb: React.SFC<BreadcrumbProps> = (props) => {
export const Breadcrumb: React.SFC<BreadcrumbProps> = (
props: BreadcrumbProps
) => {
const { direction = 'left', path, text } = props;
if (path !== undefined && text !== undefined) {
return (
......@@ -36,6 +38,7 @@ export const Breadcrumb: React.SFC<BreadcrumbProps> = (props) => {
}
return (
<div className="amundsen-breadcrumb">
{/* eslint-disable jsx-a11y/anchor-is-valid */}
<a
onClick={props.loadPreviousSearch}
className="btn btn-flat-icon title-3"
......
import * as React from 'react';
import { shallow } from 'enzyme';
import EditableSection, { EditableSectionProps } from '.';
import TagInput from 'components/Tags/TagInput';
import { ResourceType } from 'interfaces/Resources';
import EditableSection, { EditableSectionProps } from '.';
describe('EditableSection', () => {
const setup = (propOverrides?: Partial<EditableSectionProps>, children?) => {
......@@ -54,7 +54,7 @@ describe('EditableSection', () => {
.mockImplementation(() => mockTitle);
const { wrapper, props } = setup(
{ title: 'custom title' },
<TagInput resourceType={ResourceType.table} uriKey={'key'} />
<TagInput resourceType={ResourceType.table} uriKey="key" />
);
it('renders the converted props.title as the section title', () => {
......@@ -102,7 +102,7 @@ describe('EditableSection', () => {
});
it('does not render button if readOnly=true and there is no external editUrl', () => {
const wrapper = setup({ readOnly: true }, <div />).wrapper;
const { wrapper } = setup({ readOnly: true }, <div />);
expect(wrapper.find('.edit-button').exists()).toBeFalsy();
});
});
......
......@@ -3,13 +3,13 @@ import * as ReactMarkdown from 'react-markdown';
import * as autosize from 'autosize';
import { shallow } from 'enzyme';
import EditableText, { EditableTextProps } from '.';
import {
CANCEL_BUTTON_TEXT,
REFRESH_BUTTON_TEXT,
REFRESH_MESSAGE,
UPDATE_BUTTON_TEXT,
} from 'components/common/EditableText/constants';
import EditableText, { EditableTextProps } from '.';
describe('EditableText', () => {
const setup = (propOverrides?: Partial<EditableTextProps>) => {
......
......@@ -4,13 +4,13 @@ import * as ReactMarkdown from 'react-markdown';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
import { EditableSectionChildProps } from 'components/common/EditableSection';
import {
CANCEL_BUTTON_TEXT,
REFRESH_BUTTON_TEXT,
REFRESH_MESSAGE,
UPDATE_BUTTON_TEXT,
} from './constants';
import { EditableSectionChildProps } from 'components/common/EditableSection';
export interface StateFromProps {
refreshValue?: string;
......
@import 'variables';
.editable-text {
// React-Markdown
.markdown-wrapper {
font-size: 14px;
......
......@@ -8,7 +8,9 @@ export interface EntityCardProps {
sections: EntityCardSectionProps[];
}
const EntityCard: React.SFC<EntityCardProps> = ({ sections }) => {
const EntityCard: React.SFC<EntityCardProps> = ({
sections,
}: EntityCardProps) => {
const cardItems = sections.map((entry, index) => {
return (
<EntityCardSection
......
......@@ -29,7 +29,11 @@ export function convertText(str: string, caseType: string): string {
}
}
const Flag: React.SFC<FlagProps> = ({ caseType, text, labelStyle }) => {
const Flag: React.SFC<FlagProps> = ({
caseType,
text,
labelStyle,
}: FlagProps) => {
// TODO: After upgrading to Bootstrap 4, this component should leverage badges
// https://getbootstrap.com/docs/4.1/components/badge/
return (
......
......@@ -32,7 +32,7 @@ describe('FlashMessage', () => {
it('if iconClass, renders img with correct className', () => {
const testClass = 'icon-mail';
const wrapper = setup({ iconClass: testClass }).wrapper;
const { wrapper } = setup({ iconClass: testClass });
expect(wrapper.find('img').props()).toMatchObject({
className: `icon ${testClass}`,
});
......
......@@ -12,7 +12,7 @@ const FlashMessage: React.SFC<FlashMessageProps> = ({
iconClass,
message,
onClose,
}) => {
}: FlashMessageProps) => {
return (
<div className="flash-message">
{iconClass && <img className={`icon ${iconClass}`} />}
......@@ -20,7 +20,7 @@ const FlashMessage: React.SFC<FlashMessageProps> = ({
<button
type="button"
className="btn btn-close"
aria-label={'Close'}
aria-label="Close"
onClick={onClose}
/>
</div>
......
......@@ -19,7 +19,7 @@ const InfoButton: React.SFC<InfoButtonProps> = ({
infoText,
placement,
size,
}) => {
}: InfoButtonProps) => {
const popoverHoverFocus = (
<Popover id="popover-trigger-hover-focus" title={title}>
<SanitizedHTML html={infoText} />
......
......@@ -12,6 +12,7 @@ describe('CheckBoxItem', () => {
name: 'test',
value: 'testMethod',
onChange: jest.fn(),
children: <div />,
...propOverrides,
};
const wrapper = shallow(
......@@ -52,12 +53,12 @@ describe('CheckBoxItem', () => {
});
it('renders input with default value for checked if not provided', () => {
const wrapper = setup({ checked: undefined }).wrapper;
const { wrapper } = setup({ checked: undefined });
expect(wrapper.find('input').props().checked).toEqual(false);
});
it('renders input with default value for disabled if not provided', () => {
const wrapper = setup({ disabled: undefined }).wrapper;
const { wrapper } = setup({ disabled: undefined });
expect(wrapper.find('input').props().disabled).toEqual(false);
});
......
......@@ -9,6 +9,7 @@ export interface CheckBoxItemProps {
name: string;
onChange: (e: React.FormEvent<HTMLInputElement>) => any;
value: string;
children: React.ReactNode;
}
const CheckBoxItem: React.SFC<CheckBoxItemProps> = ({
......@@ -18,7 +19,7 @@ const CheckBoxItem: React.SFC<CheckBoxItemProps> = ({
onChange,
value,
children,
}) => {
}: CheckBoxItemProps) => {
return (
<div className="checkbox">
<label className="checkbox-label">
......
import * as React from 'react';
import { shallow } from 'enzyme';
import InfoButton from 'components/common/InfoButton';
import PaginatedResourceList from 'components/common/ResourceList/PaginatedResourceList';
import globalState from 'fixtures/globalState';
import {
POPULAR_TABLES_INFO_TEXT,
POPULAR_TABLES_LABEL,
POPULAR_TABLES_PER_PAGE,
POPULAR_TABLES_SOURCE_NAME,
} from './constants';
import InfoButton from 'components/common/InfoButton';
import PaginatedResourceList from 'components/common/ResourceList/PaginatedResourceList';
import globalState from 'fixtures/globalState';
import {
PopularTables,
PopularTablesProps,
mapStateToProps,
mapDispatchToProps,
} from './';
} from '.';
describe('PopularTables', () => {
const setup = (propOverrides?: Partial<PopularTablesProps>) => {
......
......@@ -3,12 +3,6 @@ import * as React from 'react';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
import {
POPULAR_TABLES_LABEL,
POPULAR_TABLES_INFO_TEXT,
POPULAR_TABLES_SOURCE_NAME,
POPULAR_TABLES_PER_PAGE,
} from './constants';
import { TableResource } from 'interfaces';
import InfoButton from 'components/common/InfoButton';
......@@ -19,6 +13,12 @@ import { GetPopularTablesRequest } from 'ducks/popularTables/types';
import { GlobalState } from 'ducks/rootReducer';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import {
POPULAR_TABLES_LABEL,
POPULAR_TABLES_INFO_TEXT,
POPULAR_TABLES_SOURCE_NAME,
POPULAR_TABLES_PER_PAGE,
} from './constants';
export interface StateFromProps {
popularTables: TableResource[];
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment