diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js index d785327431ab5..cd8012a0fa45b 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/auto_follow_pattern_list.js @@ -52,7 +52,7 @@ export class AutoFollowPatternListUI extends PureComponent {

diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/home.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/home.js index e9544cf9b2be2..68ba6e100d1ae 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/home.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/home.js @@ -11,15 +11,15 @@ import chrome from 'ui/chrome'; import { MANAGEMENT_BREADCRUMB } from 'ui/management'; import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, EuiPage, EuiPageBody, EuiPageContent, - EuiPageContentHeaderSection, - EuiTitle, EuiSpacer, - EuiPageContentHeader, - EuiButton, EuiText, + EuiTitle, } from '@elastic/eui'; import { listBreadcrumb } from '../../services/breadcrumbs'; @@ -49,37 +49,51 @@ export class CrossClusterReplicationHomeUI extends PureComponent { return ( - - - -

+ +

+ +

+
+ + + + + + +

-

+

+

-
- + + + - -
+ + +
); @@ -91,7 +105,7 @@ export class CrossClusterReplicationHomeUI extends PureComponent { return ( diff --git a/x-pack/plugins/remote_clusters/public/index.scss b/x-pack/plugins/remote_clusters/public/index.scss index 33cca267d4075..b25832255cece 100644 --- a/x-pack/plugins/remote_clusters/public/index.scss +++ b/x-pack/plugins/remote_clusters/public/index.scss @@ -17,3 +17,14 @@ max-width: 1000px !important; /* 1 */ width: 100% !important; /* 1 */ } + +/** + * 1. Override EuiFormRow styles. Otherwise the switch will jump around when toggled on and off, + * as the 'Reset to defaults' link is added to and removed from the DOM. + * 2. Fix the positioning. + */ + +.remoteClusterSkipIfUnavailableSwitch { + justify-content: flex-start !important; /* 1 */ + padding-top: $euiSizeS !important; +} diff --git a/x-pack/plugins/remote_clusters/public/sections/components/index.js b/x-pack/plugins/remote_clusters/public/sections/components/index.js new file mode 100644 index 0000000000000..a4b2c63ec552e --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/sections/components/index.js @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { RemoteClusterForm } from './remote_cluster_form'; +export { RemoteClusterPageTitle } from './remote_cluster_page_title'; diff --git a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_form/index.js b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_form/index.js similarity index 100% rename from x-pack/plugins/remote_clusters/public/sections/remote_cluster_form/index.js rename to x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_form/index.js diff --git a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_form/remote_cluster_form.js similarity index 84% rename from x-pack/plugins/remote_clusters/public/sections/remote_cluster_form/remote_cluster_form.js rename to x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_form/remote_cluster_form.js index ab7345374a1ae..6eaade2a95404 100644 --- a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_form/remote_cluster_form.js @@ -34,7 +34,9 @@ import { import { isSeedNodeValid, isSeedNodePortValid, -} from '../../services'; +} from '../../../services'; + +import { skippingDisconnectedClustersUrl } from '../../../services/documentation_links'; const defaultFields = { name: '', @@ -97,7 +99,7 @@ export class RemoteClusterFormUi extends Component { errors.seeds = ( ); } @@ -116,7 +118,7 @@ export class RemoteClusterFormUi extends Component { this.setState({ fields: newFields, fieldsErrors: this.getFieldsErrors(newFields), - areErrorsVisible: false, + // areErrorsVisible: false, }); }; @@ -138,9 +140,8 @@ export class RemoteClusterFormUi extends Component { save = () => { const { save } = this.props; - const { fieldsErrors } = this.state; - if (Object.keys(fieldsErrors).length > 0) { + if (this.hasErrors()) { this.setState({ areErrorsVisible: true, }); @@ -148,7 +149,6 @@ export class RemoteClusterFormUi extends Component { } const cluster = this.getAllFields(); - save(cluster); }; @@ -157,6 +157,10 @@ export class RemoteClusterFormUi extends Component { const errors = []; + if (!seedNode) { + return errors; + } + const isInvalid = !isSeedNodeValid(seedNode); if (isInvalid) { @@ -172,7 +176,7 @@ export class RemoteClusterFormUi extends Component { if (isPortInvalid) { errors.push(intl.formatMessage({ id: 'xpack.remoteClusters.remoteClusterForm.localSeedError.invalidPortMessage', - defaultMessage: 'Seed node must define a numeric port.', + defaultMessage: 'A port is required.', })); } @@ -180,6 +184,11 @@ export class RemoteClusterFormUi extends Component { }; onCreateSeed = (newSeed) => { + // If the user just hit enter without typing anything, treat it as a no-op. + if (!newSeed) { + return; + } + const localSeedErrors = this.getLocalSeedErrors(newSeed); if (localSeedErrors.length !== 0) { @@ -246,6 +255,13 @@ export class RemoteClusterFormUi extends Component { }); }; + hasErrors = () => { + const { fieldsErrors, localSeedErrors } = this.state; + const errorValues = Object.values(fieldsErrors); + const hasErrors = errorValues.some(error => error !== undefined) || localSeedErrors.length; + return hasErrors; + }; + renderSeeds() { const { areErrorsVisible, @@ -274,7 +290,7 @@ export class RemoteClusterFormUi extends Component {

@@ -284,9 +300,8 @@ export class RemoteClusterFormUi extends Component {

@@ -297,14 +312,13 @@ export class RemoteClusterFormUi extends Component { label={( )} helpText={( )} isInvalid={showErrors} @@ -315,7 +329,7 @@ export class RemoteClusterFormUi extends Component { noSuggestions placeholder={intl.formatMessage({ id: 'xpack.remoteClusters.remoteClusterForm.fieldSeedsPlaceholder', - defaultMessage: '127.0.0.1:9400', + defaultMessage: 'host:port', })} selectedOptions={formattedSeeds} onCreateOption={this.onCreateSeed} @@ -343,7 +357,7 @@ export class RemoteClusterFormUi extends Component {

@@ -353,10 +367,27 @@ export class RemoteClusterFormUi extends Component {

+ + + ), + learnMoreLink: ( + + + + ), + }} />

@@ -364,6 +395,7 @@ export class RemoteClusterFormUi extends Component { fullWidth > @@ -437,6 +472,7 @@ export class RemoteClusterFormUi extends Component { iconType="check" onClick={this.save} fill + disabled={isSaveDisabled} > { - const { areErrorsVisible, fieldsErrors } = this.state; - const errorValues = Object.values(fieldsErrors); - const hasErrors = errorValues.some(error => error !== undefined); + const { areErrorsVisible } = this.state; + const hasErrors = this.hasErrors(); if (!areErrorsVisible || !hasErrors) { return null; @@ -518,7 +553,7 @@ export class RemoteClusterFormUi extends Component { title={( )} color="danger" @@ -564,8 +599,7 @@ export class RemoteClusterFormUi extends Component { description={( )} fullWidth diff --git a/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_page_title/index.js b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_page_title/index.js new file mode 100644 index 0000000000000..316cb05087725 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_page_title/index.js @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { RemoteClusterPageTitle } from './remote_cluster_page_title'; diff --git a/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_page_title/remote_cluster_page_title.js b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_page_title/remote_cluster_page_title.js new file mode 100644 index 0000000000000..f8bd3535b34f1 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/sections/components/remote_cluster_page_title/remote_cluster_page_title.js @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiPageContentHeader, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; + +import { remoteClustersUrl } from '../../../services/documentation_links'; + +export const RemoteClusterPageTitle = ({ title }) => ( + + + + + + + +

{title}

+
+
+ + + + + + +
+
+
+); + +RemoteClusterPageTitle.propTypes = { + title: PropTypes.node.isRequired, +}; diff --git a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_add/remote_cluster_add.js b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_add/remote_cluster_add.js index 5729eb950ff3f..9b9989705578b 100644 --- a/x-pack/plugins/remote_clusters/public/sections/remote_cluster_add/remote_cluster_add.js +++ b/x-pack/plugins/remote_clusters/public/sections/remote_cluster_add/remote_cluster_add.js @@ -14,14 +14,11 @@ import { EuiPage, EuiPageBody, EuiPageContent, - EuiPageContentHeader, - EuiSpacer, - EuiTitle, } from '@elastic/eui'; import { CRUD_APP_BASE_PATH } from '../../constants'; import { listBreadcrumb, addBreadcrumb } from '../../services'; -import { RemoteClusterForm } from '../remote_cluster_form'; +import { RemoteClusterPageTitle, RemoteClusterForm } from '../components'; export class RemoteClusterAddUi extends Component { static propTypes = { @@ -61,18 +58,14 @@ export class RemoteClusterAddUi extends Component { horizontalPosition="center" className="remoteClusterAddPage" > - - - - -

- -

-
-
+ + )} + /> - - - - -

- -

-
-
+ + )} + /> {this.renderContent()} diff --git a/x-pack/plugins/remote_clusters/public/services/documentation_links.js b/x-pack/plugins/remote_clusters/public/services/documentation_links.js new file mode 100644 index 0000000000000..a7ccaa7df8800 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/services/documentation_links.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; + +const esBase = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; + +export const skippingDisconnectedClustersUrl = `${esBase}/modules-cross-cluster-search.html#_skipping_disconnected_clusters`; +export const remoteClustersUrl = `${esBase}/modules-remote-clusters.html`;