Remove injectI18n in dashboard plugin. (#44580) (#44919)

* Removed injectI18n from dashboard listing

* Added helpers for I18nProvider

* Remove intl from DashboardCloneModal

* Remove intl from options.tsx

* Remove intl from DashboardSaveModal
This commit is contained in:
Stacey Gammon 2019-09-05 18:07:36 -04:00 committed by GitHub
parent b4d200c172
commit 65850c7c6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 159 additions and 162 deletions

View file

@ -19,14 +19,10 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import {
EuiLink,
EuiButton,
EuiEmptyPrompt,
} from '@elastic/eui';
import { EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui';
import { TableListView } from './../../table_list_view';
@ -37,8 +33,7 @@ export const EMPTY_FILTER = '';
// and not supporting server-side paging.
// This component does not try to tackle these problems (yet) and is just feature matching the legacy component
// TODO support server side sorting/paging once title and description are sortable on the server.
class DashboardListingUi extends React.Component {
export class DashboardListing extends React.Component {
constructor(props) {
super(props);
}
@ -54,21 +49,15 @@ class DashboardListingUi extends React.Component {
listingLimit={this.props.listingLimit}
initialFilter={this.props.initialFilter}
noItemsFragment={this.getNoItemsMessage()}
entityName={
i18n.translate('kbn.dashboard.listing.table.entityName', {
defaultMessage: 'dashboard'
})
}
entityNamePlural={
i18n.translate('kbn.dashboard.listing.table.entityNamePlural', {
defaultMessage: 'dashboards'
})
}
tableListTitle={
i18n.translate('kbn.dashboard.listing.dashboardsTitle', {
defaultMessage: 'Dashboards'
})
}
entityName={i18n.translate('kbn.dashboard.listing.table.entityName', {
defaultMessage: 'dashboard',
})}
entityNamePlural={i18n.translate('kbn.dashboard.listing.table.entityNamePlural', {
defaultMessage: 'dashboards',
})}
tableListTitle={i18n.translate('kbn.dashboard.listing.dashboardsTitle', {
defaultMessage: 'Dashboards',
})}
/>
);
}
@ -146,7 +135,6 @@ class DashboardListingUi extends React.Component {
/>
</div>
);
}
getTableColumns() {
@ -154,7 +142,7 @@ class DashboardListingUi extends React.Component {
{
field: 'title',
name: i18n.translate('kbn.dashboard.listing.table.titleColumnName', {
defaultMessage: 'Title'
defaultMessage: 'Title',
}),
sortable: true,
render: (field, record) => (
@ -164,22 +152,22 @@ class DashboardListingUi extends React.Component {
>
{field}
</EuiLink>
)
),
},
{
field: 'description',
name: i18n.translate('kbn.dashboard.listing.table.descriptionColumnName', {
defaultMessage: 'Description'
defaultMessage: 'Description',
}),
dataType: 'string',
sortable: true,
}
},
];
return tableColumns;
}
}
DashboardListingUi.propTypes = {
DashboardListing.propTypes = {
createItem: PropTypes.func.isRequired,
findItems: PropTypes.func.isRequired,
deleteItems: PropTypes.func.isRequired,
@ -190,8 +178,6 @@ DashboardListingUi.propTypes = {
initialFilter: PropTypes.string,
};
DashboardListingUi.defaultProps = {
DashboardListing.defaultProps = {
initialFilter: EMPTY_FILTER,
};
export const DashboardListing = injectI18n(DashboardListingUi);

View file

@ -17,63 +17,54 @@
* under the License.
*/
jest.mock('ui/notify',
jest.mock(
'ui/notify',
() => ({
toastNotifications: {
addWarning: () => {},
}
}), { virtual: true });
},
}),
{ virtual: true }
);
jest.mock('lodash',
jest.mock(
'lodash',
() => ({
...require.requireActual('lodash'),
// mock debounce to fire immediately with no internal timer
debounce: function (func) {
debounce: func => {
function debounced(...args) {
return func.apply(this, args);
}
return debounced;
}
}), { virtual: true });
},
}),
{ virtual: true }
);
import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { shallow } from 'enzyme';
import {
DashboardListing,
} from './dashboard_listing';
import { DashboardListing } from './dashboard_listing';
const find = (num) => {
const find = num => {
const hits = [];
for (let i = 0; i < num; i++) {
hits.push({
id: `dashboard${i}`,
title: `dashboard${i} title`,
description: `dashboard${i} desc`
description: `dashboard${i} desc`,
});
}
return Promise.resolve({
total: num,
hits: hits
hits: hits,
});
};
test('renders empty page in before initial fetch to avoid flickering', () => {
const component = shallowWithIntl(<DashboardListing.WrappedComponent
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1000}
hideWriteControls={false}
/>);
expect(component).toMatchSnapshot();
});
describe('after fetch', () => {
test('initialFilter', async () => {
const component = shallowWithIntl(<DashboardListing.WrappedComponent
const component = shallow(
<DashboardListing
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
@ -81,8 +72,25 @@ describe('after fetch', () => {
getViewUrl={() => {}}
listingLimit={1000}
hideWriteControls={false}
initialFilter="my dashboard"
/>);
/>
);
expect(component).toMatchSnapshot();
});
describe('after fetch', () => {
test('initialFilter', async () => {
const component = shallow(
<DashboardListing
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1000}
hideWriteControls={false}
initialFilter="my dashboard"
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
@ -93,15 +101,17 @@ describe('after fetch', () => {
});
test('renders table rows', async () => {
const component = shallowWithIntl(<DashboardListing.WrappedComponent
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1000}
hideWriteControls={false}
/>);
const component = shallow(
<DashboardListing
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1000}
hideWriteControls={false}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
@ -112,15 +122,17 @@ describe('after fetch', () => {
});
test('renders call to action when no dashboards exist', async () => {
const component = shallowWithIntl(<DashboardListing.WrappedComponent
findItems={find.bind(null, 0)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1}
hideWriteControls={false}
/>);
const component = shallow(
<DashboardListing
findItems={find.bind(null, 0)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1}
hideWriteControls={false}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
@ -131,15 +143,17 @@ describe('after fetch', () => {
});
test('hideWriteControls', async () => {
const component = shallowWithIntl(<DashboardListing.WrappedComponent
findItems={find.bind(null, 0)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1}
hideWriteControls={true}
/>);
const component = shallow(
<DashboardListing
findItems={find.bind(null, 0)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1}
hideWriteControls={true}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));
@ -150,15 +164,17 @@ describe('after fetch', () => {
});
test('renders warning when listingLimit is exceeded', async () => {
const component = shallowWithIntl(<DashboardListing.WrappedComponent
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1}
hideWriteControls={false}
/>);
const component = shallow(
<DashboardListing
findItems={find.bind(null, 2)}
deleteItems={() => {}}
createItem={() => {}}
editItem={() => {}}
getViewUrl={() => {}}
listingLimit={1}
hideWriteControls={false}
/>
);
// Ensure all promises resolve
await new Promise(resolve => process.nextTick(resolve));

View file

@ -19,55 +19,48 @@
import React from 'react';
import sinon from 'sinon';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import {
findTestSubject,
} from '@elastic/eui/lib/test';
import { shallowWithI18nProvider, mountWithI18nProvider } from 'test_utils/enzyme_helpers';
import { findTestSubject } from '@elastic/eui/lib/test';
import {
DashboardCloneModal,
} from './clone_modal';
import { DashboardCloneModal } from './clone_modal';
let onClone;
let onClose;
let component;
beforeEach(() => {
onClone = sinon.spy();
onClose = sinon.spy();
});
function createComponent(creationMethod = mountWithIntl) {
component = creationMethod(
<DashboardCloneModal.WrappedComponent
title="dash title"
onClose={onClose}
onClone={onClone}
/>
);
}
test('renders DashboardCloneModal', () => {
createComponent(shallowWithIntl);
const component = shallowWithI18nProvider(
<DashboardCloneModal title="dash title" onClose={onClose} onClone={onClone} />
);
expect(component).toMatchSnapshot(); // eslint-disable-line
});
test('onClone', () => {
createComponent();
const component = mountWithI18nProvider(
<DashboardCloneModal title="dash title" onClose={onClose} onClone={onClone} />
);
findTestSubject(component, 'cloneConfirmButton').simulate('click');
sinon.assert.calledWith(onClone, 'dash title');
sinon.assert.notCalled(onClose);
});
test('onClose', () => {
createComponent();
const component = mountWithI18nProvider(
<DashboardCloneModal title="dash title" onClose={onClose} onClone={onClone} />
);
findTestSubject(component, 'cloneCancelButton').simulate('click');
sinon.assert.calledOnce(onClose);
sinon.assert.notCalled(onClone);
});
test('title', () => {
createComponent();
const component = mountWithI18nProvider(
<DashboardCloneModal title="dash title" onClose={onClose} onClone={onClone} />
);
const event = { target: { value: 'a' } };
component.find('input').simulate('change', event);
findTestSubject(component, 'cloneConfirmButton').simulate('click');

View file

@ -18,7 +18,8 @@
*/
import React, { Fragment } from 'react';
import { injectI18n, FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import {
EuiButton,
@ -43,7 +44,6 @@ interface Props {
) => Promise<void>;
onClose: () => void;
title: string;
intl: InjectedIntl;
}
interface State {
@ -53,7 +53,7 @@ interface State {
isLoading: boolean;
}
class DashboardCloneModalUi extends React.Component<Props, State> {
export class DashboardCloneModal extends React.Component<Props, State> {
private isMounted = false;
constructor(props: Props) {
@ -117,15 +117,12 @@ class DashboardCloneModalUi extends React.Component<Props, State> {
<EuiSpacer />
<EuiCallOut
size="s"
title={this.props.intl.formatMessage(
{
id: 'kbn.dashboard.topNav.cloneModal.dashboardExistsTitle',
defaultMessage: 'A dashboard with the title {newDashboardName} already exists.',
},
{
title={i18n.translate('kbn.dashboard.topNav.cloneModal.dashboardExistsTitle', {
defaultMessage: 'A dashboard with the title {newDashboardName} already exists.',
values: {
newDashboardName: `'${this.state.newDashboardName}'`,
}
)}
},
})}
color="warning"
data-test-subj="titleDupicateWarnMsg"
>
@ -215,5 +212,3 @@ class DashboardCloneModalUi extends React.Component<Props, State> {
);
}
}
export const DashboardCloneModal = injectI18n(DashboardCloneModalUi);

View file

@ -18,7 +18,7 @@
*/
import React, { Component } from 'react';
import { injectI18n, InjectedIntl } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { EuiForm, EuiFormRow, EuiSwitch } from '@elastic/eui';
@ -27,7 +27,6 @@ interface Props {
onUseMarginsChange: (useMargins: boolean) => void;
hidePanelTitles: boolean;
onHidePanelTitlesChange: (hideTitles: boolean) => void;
intl: InjectedIntl;
}
interface State {
@ -35,7 +34,7 @@ interface State {
hidePanelTitles: boolean;
}
class OptionsMenuUi extends Component<Props, State> {
export class OptionsMenu extends Component<Props, State> {
state = {
useMargins: this.props.useMargins,
hidePanelTitles: this.props.hidePanelTitles,
@ -62,10 +61,12 @@ class OptionsMenuUi extends Component<Props, State> {
<EuiForm data-test-subj="dashboardOptionsMenu">
<EuiFormRow>
<EuiSwitch
label={this.props.intl.formatMessage({
id: 'kbn.dashboard.topNav.options.useMarginsBetweenPanelsSwitchLabel',
defaultMessage: 'Use margins between panels',
})}
label={i18n.translate(
'kbn.dashboard.topNav.options.useMarginsBetweenPanelsSwitchLabel',
{
defaultMessage: 'Use margins between panels',
}
)}
checked={this.state.useMargins}
onChange={this.handleUseMarginsChange}
data-test-subj="dashboardMarginsCheckbox"
@ -74,8 +75,7 @@ class OptionsMenuUi extends Component<Props, State> {
<EuiFormRow>
<EuiSwitch
label={this.props.intl.formatMessage({
id: 'kbn.dashboard.topNav.options.hideAllPanelTitlesSwitchLabel',
label={i18n.translate('kbn.dashboard.topNav.options.hideAllPanelTitlesSwitchLabel', {
defaultMessage: 'Show panel titles',
})}
checked={!this.state.hidePanelTitles}
@ -87,5 +87,3 @@ class OptionsMenuUi extends Component<Props, State> {
);
}
}
export const OptionsMenu = injectI18n(OptionsMenuUi);

View file

@ -18,20 +18,20 @@
*/
import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers';
import {
DashboardSaveModal,
} from './save_modal';
import { DashboardSaveModal } from './save_modal';
test('renders DashboardSaveModal', () => {
const component = shallowWithIntl(<DashboardSaveModal.WrappedComponent
onSave={() => {}}
onClose={() => {}}
title="dash title"
description="dash description"
timeRestore={true}
showCopyOnSave={true}
/>);
const component = shallowWithI18nProvider(
<DashboardSaveModal
onSave={() => {}}
onClose={() => {}}
title="dash title"
description="dash description"
timeRestore={true}
showCopyOnSave={true}
/>
);
expect(component).toMatchSnapshot(); // eslint-disable-line
});

View file

@ -18,7 +18,7 @@
*/
import React, { Fragment } from 'react';
import { injectI18n, FormattedMessage, InjectedIntl } from '@kbn/i18n/react';
import { FormattedMessage } from '@kbn/i18n/react';
import { SavedObjectSaveModal } from 'ui/saved_objects/components/saved_object_save_modal';
import { EuiFormRow, EuiTextArea, EuiSwitch } from '@elastic/eui';
@ -46,7 +46,6 @@ interface Props {
description: string;
timeRestore: boolean;
showCopyOnSave: boolean;
intl: InjectedIntl;
}
interface State {
@ -54,7 +53,7 @@ interface State {
timeRestore: boolean;
}
class DashboardSaveModalUi extends React.Component<Props, State> {
export class DashboardSaveModal extends React.Component<Props, State> {
state: State = {
description: this.props.description,
timeRestore: this.props.timeRestore,
@ -152,5 +151,3 @@ class DashboardSaveModalUi extends React.Component<Props, State> {
);
}
}
export const DashboardSaveModal = injectI18n(DashboardSaveModalUi);

View file

@ -128,3 +128,15 @@ export function renderWithIntl<T>(
}
export const nextTick = () => new Promise(res => process.nextTick(res));
export function shallowWithI18nProvider<T>(child: ReactElement<T>) {
const wrapped = shallow(<I18nProvider>{child}</I18nProvider>);
const name = typeof child.type === 'string' ? child.type : child.type.name;
return wrapped.find(name).dive();
}
export function mountWithI18nProvider<T>(child: ReactElement<T>) {
const wrapped = mount(<I18nProvider>{child}</I18nProvider>);
const name = typeof child.type === 'string' ? child.type : child.type.name;
return wrapped.find(name);
}