Skip to content

Commit

Permalink
[Code] Code Integrator Component (#47180) (#47521)
Browse files Browse the repository at this point in the history
* Add CodeIntegrator component

* Abstracts shared ImportModal component
* Uses placeholder data and callbacks in lieu of real integration
  • Loading branch information
rylnd authored Oct 7, 2019
1 parent 6dd5c32 commit 8f3a683
Show file tree
Hide file tree
Showing 11 changed files with 292 additions and 95 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,13 @@

import {
EuiButton,
EuiButtonEmpty,
EuiFieldText,
EuiFlexGroup,
EuiFlexItem,
EuiForm,
EuiFormRow,
EuiGlobalToastList,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiOverlayMask,
EuiSpacer,
EuiSuperSelect,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
Expand All @@ -39,6 +29,7 @@ import { ToastType } from '../../reducers/repository_management';
import { isImportRepositoryURLInvalid } from '../../utils/url';
import { ProjectItem } from './project_item';
import { ProjectSettings } from './project_settings';
import { ImportModal } from '../integrations/import_modal';

enum SortOptionsValue {
AlphabeticalAsc = 'alphabetical_asc',
Expand Down Expand Up @@ -169,70 +160,21 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
}
};

public updateIsInvalid = () => {
this.setState({ isInvalid: isImportRepositoryURLInvalid(this.state.repoURL) });
};

public renderImportModal = () => {
return (
<EuiOverlayMask>
<EuiModal onClose={this.closeModal}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<FormattedMessage
id="xpack.code.adminPage.repoTab.importRepoTitle"
defaultMessage="Import a new repo"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiTitle size="xs">
<h3>
<FormattedMessage
id="xpack.code.adminPage.repoTab.repositoryUrlFormLabel"
defaultMessage="Repository URL"
/>
</h3>
</EuiTitle>
<EuiForm>
<EuiFormRow
isInvalid={this.state.isInvalid}
error={i18n.translate('xpack.code.adminPage.repoTab.repositoryUrlEmptyText', {
defaultMessage: "The URL shouldn't be empty.",
})}
>
<EuiFieldText
value={this.state.repoURL}
onChange={this.onChange}
onBlur={this.updateIsInvalid}
placeholder="https://github.com/Microsoft/TypeScript-Node-Starter"
aria-label="input project url"
data-test-subj="importRepositoryUrlInputBox"
isLoading={this.props.importLoading}
fullWidth={true}
isInvalid={this.state.isInvalid}
autoFocus={true}
/>
</EuiFormRow>
</EuiForm>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={this.closeModal}>
<FormattedMessage
id="xpack.code.adminPage.repoTab.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
<EuiButton fill onClick={this.submitImportProject} disabled={this.props.importLoading}>
<FormattedMessage
id="xpack.code.adminPage.repoTab.importButtonLabel"
defaultMessage="Import"
/>
</EuiButton>
</EuiModalFooter>
</EuiModal>
</EuiOverlayMask>
);
const { isInvalid, repoURL, showImportProjectModal } = this.state;

if (showImportProjectModal) {
return (
<ImportModal
isInvalid={isInvalid}
isLoading={this.props.importLoading}
onChange={this.onChange}
onClose={this.closeModal}
onSubmit={this.submitImportProject}
value={repoURL}
/>
);
}
};

public setSortOption = (value: string) => {
Expand All @@ -242,7 +184,6 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
public render() {
const { projects, status, toastMessage, showToast, toastType } = this.props;
const projectsCount = projects.length;
const modal = this.state.showImportProjectModal && this.renderImportModal();
const sortedProjects = projects.sort(sortFunctionsFactory(status)[this.state.sortOption]);

const repoList = sortedProjects.map((repo: Repository) => (
Expand Down Expand Up @@ -321,7 +262,7 @@ class CodeProjectTab extends React.PureComponent<Props, State> {
</EuiText>
<EuiSpacer />
{repoList}
{modal}
{this.renderImportModal()}
{settings}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
border-radius: $euiSizeXS $euiSizeXS 0 0;
}

.referencesPanel__code-block {
margin-bottom: $euiSizeXL;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* 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, { useState } from 'react';
import { EuiButtonEmpty, EuiPopover, EuiText } from '@elastic/eui';

import { RepoSelector } from './repo_selector';

interface Props {
onRepoSelect: (repo: string) => void;
onImportSuccess: (repo: string) => void;
repos: string[];
}

export const CodeIntegrator = ({ onRepoSelect, onImportSuccess, repos }: Props) => {
const [showSelector, setShowSelector] = useState(false);

const handleClick = () => setShowSelector(true);

const handleSelect = (codeId: string) => {
onRepoSelect(codeId);
setShowSelector(false);
// TODO: show success
};

const link = (
<EuiButtonEmpty
className="codeIntegrations__link--external"
iconType="codeApp"
onClick={handleClick}
>
<EuiText size="s">View in Code</EuiText>
</EuiButtonEmpty>
);

return (
<EuiPopover
anchorPosition="leftCenter"
button={link}
isOpen={showSelector}
closePopover={() => setShowSelector(false)}
>
<EuiText size="s" className="codeIntegrations__popover">
<h3>No repository mapping found</h3>
<p>
We can't find the mapping between service and the source code. Select the repository or
import a new one.
</p>
</EuiText>
<RepoSelector onSelect={handleSelect} onImport={onImportSuccess} repos={repos} />
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const FrameHeader = ({
onClick: () => void;
}) => (
<EuiFlexGroup
className="integrations__snippet-info"
className="codeIntegrations__snippet-info"
alignItems="center"
justifyContent="spaceBetween"
gutterSize="none"
Expand All @@ -31,7 +31,7 @@ export const FrameHeader = ({
<EuiText size="xs">
<EuiTextColor color="subdued">Last updated: 14 mins ago</EuiTextColor>
<EuiButtonIcon
className="integrations__link--external integrations__button-icon"
className="codeIntegrations__link--external codeIntegrations__button-icon"
iconType="codeApp"
onClick={onClick}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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, { ChangeEvent } from 'react';

import {
EuiButton,
EuiButtonEmpty,
EuiFieldText,
EuiForm,
EuiFormRow,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiOverlayMask,
EuiTitle,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';

interface Props {
isInvalid: boolean;
isLoading: boolean;
onChange: (e: ChangeEvent<HTMLInputElement>) => void;
onClose: () => void;
onSubmit: () => void;
value: string;
}

export const ImportModal = ({
isInvalid,
isLoading,
onChange,
onClose,
onSubmit,
value,
}: Props) => (
<EuiOverlayMask>
<EuiModal onClose={onClose}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<FormattedMessage
id="xpack.code.adminPage.repoTab.importRepoTitle"
defaultMessage="Import a new repo"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiTitle size="xs">
<h3>
<FormattedMessage
id="xpack.code.adminPage.repoTab.repositoryUrlFormLabel"
defaultMessage="Repository URL"
/>
</h3>
</EuiTitle>
<EuiForm>
<EuiFormRow
isInvalid={isInvalid}
error={i18n.translate('xpack.code.adminPage.repoTab.repositoryUrlEmptyText', {
defaultMessage: "The URL shouldn't be empty.",
})}
>
<EuiFieldText
value={value}
onChange={onChange}
onBlur={onChange}
placeholder="https://github.com/Microsoft/TypeScript-Node-Starter"
aria-label="input project url"
data-test-subj="importRepositoryUrlInputBox"
isLoading={isLoading}
fullWidth={true}
isInvalid={isInvalid}
autoFocus={true}
/>
</EuiFormRow>
</EuiForm>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={onClose}>
<FormattedMessage
id="xpack.code.adminPage.repoTab.cancelButtonLabel"
defaultMessage="Cancel"
/>
</EuiButtonEmpty>
<EuiButton fill onClick={onSubmit} disabled={isLoading}>
<FormattedMessage
id="xpack.code.adminPage.repoTab.importButtonLabel"
defaultMessage="Import"
/>
</EuiButton>
</EuiModalFooter>
</EuiModal>
</EuiOverlayMask>
);
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ import { CodeBlock } from '../codeblock/codeblock';
import { history } from '../../utils/url';
import { FrameHeader } from './frame_header';
import { RepoTitle } from './repo_title';
import { CodeIntegrator } from './code_integrator';
import { externalFileURI } from './helpers';
import { frames, results } from './data';
import { frames, Frame, repos, results } from './data';

const associateToService = (frame: Frame) => (repo: string) =>
alert(`repo ${repo} associated with service ${JSON.stringify(frame)}`);

const handleImport = (repo: string) => alert(`import done: ${repo}`);

export const Integrations = () => (
<div className="codeContainer__root integrations__container">
<div className="codeContainer__root codeIntegrations__container">
{frames.map(frame => {
const { fileName, lineNumber } = frame;
const key = `${fileName}#L${lineNumber}`;
Expand All @@ -27,7 +33,7 @@ export const Integrations = () => (
const fileUrl = externalFileURI(uri, filePath);

return (
<div key={key} className="integrations__frame">
<div key={key} className="codeIntegrations__frame">
<RepoTitle uri={snippet.uri} />
<CodeBlock
content={content}
Expand All @@ -46,13 +52,18 @@ export const Integrations = () => (
}

return (
<div key={key} className="integrations__frame">
<div key={key} className="codeIntegrations__frame">
<EuiFlexGroup justifyContent="spaceBetween" gutterSize="none" alignItems="center">
<EuiText size="s" className="integrations__code">
<EuiText size="s" className="codeIntegrations__code">
<span>{fileName}</span>
<span className="integrations__preposition">at</span>
<span className="codeIntegrations__preposition">at</span>
<span>line {lineNumber}</span>
</EuiText>
<CodeIntegrator
onRepoSelect={associateToService(frame)}
onImportSuccess={handleImport}
repos={repos}
/>
</EuiFlexGroup>
</div>
);
Expand Down
Loading

0 comments on commit 8f3a683

Please sign in to comment.