From 67d026e77f6ab0b16af4fd7ce96c6f92483b5d39 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Fri, 4 Oct 2019 20:26:55 -0500 Subject: [PATCH] [Code] Code Integrator Component (#47180) * Add CodeIntegrator component * Abstracts shared ImportModal component * Uses placeholder data and callbacks in lieu of real integration --- .../components/admin_page/project_tab.tsx | 91 +++------------- .../components/editor/references_panel.scss | 3 + .../integrations/code_integrator.tsx | 56 ++++++++++ .../components/integrations/frame_header.tsx | 4 +- .../components/integrations/import_modal.tsx | 100 ++++++++++++++++++ .../public/components/integrations/index.tsx | 23 ++-- .../components/integrations/integrations.scss | 20 ++-- .../components/integrations/repo_selector.tsx | 82 ++++++++++++++ .../components/integrations/repo_title.tsx | 4 +- .../public/components/search_page/search.scss | 3 + x-pack/legacy/plugins/code/public/index.scss | 1 + 11 files changed, 292 insertions(+), 95 deletions(-) create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx create mode 100644 x-pack/legacy/plugins/code/public/components/search_page/search.scss diff --git a/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx b/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx index 56f518fc82aa..3bca381d741d 100644 --- a/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx +++ b/x-pack/legacy/plugins/code/public/components/admin_page/project_tab.tsx @@ -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'; @@ -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', @@ -169,70 +160,21 @@ class CodeProjectTab extends React.PureComponent { } }; - public updateIsInvalid = () => { - this.setState({ isInvalid: isImportRepositoryURLInvalid(this.state.repoURL) }); - }; - public renderImportModal = () => { - return ( - - - - - - - - - -

- -

-
- - - - - -
- - - - - - - - -
-
- ); + const { isInvalid, repoURL, showImportProjectModal } = this.state; + + if (showImportProjectModal) { + return ( + + ); + } }; public setSortOption = (value: string) => { @@ -242,7 +184,6 @@ class CodeProjectTab extends React.PureComponent { 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) => ( @@ -321,7 +262,7 @@ class CodeProjectTab extends React.PureComponent { {repoList} - {modal} + {this.renderImportModal()} {settings} ); diff --git a/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss b/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss index fc3df8bb79a4..13d1c45e6166 100644 --- a/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss +++ b/x-pack/legacy/plugins/code/public/components/editor/references_panel.scss @@ -31,3 +31,6 @@ border-radius: $euiSizeXS $euiSizeXS 0 0; } +.referencesPanel__code-block { + margin-bottom: $euiSizeXL; +} diff --git a/x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx b/x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx new file mode 100644 index 000000000000..7df7a976be69 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/code_integrator.tsx @@ -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 = ( + + View in Code + + ); + + return ( + setShowSelector(false)} + > + +

No repository mapping found

+

+ We can't find the mapping between service and the source code. Select the repository or + import a new one. +

+
+ +
+ ); +}; diff --git a/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx b/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx index 0fa482f8bce3..c63faf75603d 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/frame_header.tsx @@ -18,7 +18,7 @@ export const FrameHeader = ({ onClick: () => void; }) => ( Last updated: 14 mins ago diff --git a/x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx b/x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx new file mode 100644 index 000000000000..de526d4046e2 --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/import_modal.tsx @@ -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) => void; + onClose: () => void; + onSubmit: () => void; + value: string; +} + +export const ImportModal = ({ + isInvalid, + isLoading, + onChange, + onClose, + onSubmit, + value, +}: Props) => ( + + + + + + + + + +

+ +

+
+ + + + + +
+ + + + + + + + +
+
+); diff --git a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx index 5052b3502748..25eeb6fbe1a2 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/index.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/index.tsx @@ -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 = () => ( -
+
{frames.map(frame => { const { fileName, lineNumber } = frame; const key = `${fileName}#L${lineNumber}`; @@ -27,7 +33,7 @@ export const Integrations = () => ( const fileUrl = externalFileURI(uri, filePath); return ( -
+
( } return ( -
+
- + {fileName} - at + at line {lineNumber} +
); diff --git a/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss b/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss index 24fe406cd096..98330c406082 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss +++ b/x-pack/legacy/plugins/code/public/components/integrations/integrations.scss @@ -1,43 +1,43 @@ -.integrations__container { +.codeIntegrations__container { padding: $euiSize; } -.integrations__frame { +.codeIntegrations__frame { margin: $euiSizeS 0; } -.integrations__code { +.codeIntegrations__code { @include euiCodeFont; } -.integrations__link--external { +.codeIntegrations__link--external { margin-left: $euiSizeS; } -.integrations__preposition { +.codeIntegrations__preposition { margin: 0 $euiSizeS; color: $euiColorMediumShade; } -.integrations__button-icon { +.codeIntegrations__button-icon { padding: $euiSizeXS; background-color: $euiColorLightestShade; border: 1px solid $euiColorLightShade; } -.integrations__snippet-info { +.codeIntegrations__snippet-info { margin-bottom: $euiSizeS; } -.integrations__snippet-title { +.codeIntegrations__snippet-title { margin-bottom: $euiSizeS; } -.integrations__text--bold { +.codeIntegrations__text--bold { font-weight: $euiFontWeightBold; } -.integrations__popover { +.codeIntegrations__popover { margin-bottom: 1rem; width: 300px; } diff --git a/x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx b/x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx new file mode 100644 index 000000000000..8aeba2b600ed --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/integrations/repo_selector.tsx @@ -0,0 +1,82 @@ +/* + * 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 { EuiButton, EuiSelect } from '@elastic/eui'; +import { ImportModal } from './import_modal'; +import { isImportRepositoryURLInvalid } from '../../utils/url'; + +interface Props { + onSelect: (repo: string) => void; + onImport: (repo: string) => void; + repos: string[]; +} + +const placeHolderOption = { value: 'select_new', text: 'Select' }; +const importNewOption = { value: 'import_new', text: 'Import new' }; +const importStub: (repo: string) => Promise = repo => + new Promise(resolve => setTimeout(() => resolve(repo), 5000)); + +export const RepoSelector = ({ onImport, onSelect, repos: _repos }: Props) => { + const [selectedValue, setSelectedValue] = useState(placeHolderOption.value); + const [newRepo, setNewRepo] = useState(''); + const [isInvalid, setIsInvalid] = useState(false); + const [showModal, setShowModal] = useState(false); + + const repos = newRepo ? [..._repos, newRepo] : _repos; + const selectedRepo = repos.find(repo => repo === selectedValue); + + const options = [ + placeHolderOption, + ...repos.map(repo => ({ value: repo, text: repo })), + importNewOption, + ]; + + const handleNewRepoChange = ({ target: { value } }: React.ChangeEvent) => { + setIsInvalid(isImportRepositoryURLInvalid(value)); + setNewRepo(value); + }; + + const handleChange = ({ target: { value } }: React.ChangeEvent) => { + setSelectedValue(value); + + if (value === 'import_new') { + setShowModal(true); + } + }; + + const handleSave = () => selectedRepo && onSelect(selectedRepo); + + const handleImportSubmit = () => { + setSelectedValue(newRepo); + importStub(newRepo).then(onImport); + setShowModal(false); + }; + + const handleClose = () => { + setSelectedValue(placeHolderOption.value); + setShowModal(false); + }; + + return ( + <> + + + Save Mapping + + {showModal && ( + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx b/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx index 2ed3d529b699..f3577d5f6426 100644 --- a/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx +++ b/x-pack/legacy/plugins/code/public/components/integrations/repo_title.tsx @@ -14,9 +14,9 @@ export const RepoTitle = ({ uri }: { uri: string }) => { const name = RepositoryUtils.repoNameFromUri(uri); return ( - + {org}/ - {name} + {name} ); }; diff --git a/x-pack/legacy/plugins/code/public/components/search_page/search.scss b/x-pack/legacy/plugins/code/public/components/search_page/search.scss new file mode 100644 index 000000000000..febad588af3c --- /dev/null +++ b/x-pack/legacy/plugins/code/public/components/search_page/search.scss @@ -0,0 +1,3 @@ +.codeResult__code-block { + margin-bottom: $euiSizeXL; +} diff --git a/x-pack/legacy/plugins/code/public/index.scss b/x-pack/legacy/plugins/code/public/index.scss index 6d996a80f283..43768f7758ea 100644 --- a/x-pack/legacy/plugins/code/public/index.scss +++ b/x-pack/legacy/plugins/code/public/index.scss @@ -4,6 +4,7 @@ @import "./monaco/override_monaco_styles.scss"; @import "./components/diff_page/diff.scss"; @import "./components/main/main.scss"; +@import "./components/search_page/search.scss"; @import "./components/integrations/integrations.scss"; // TODO: Cleanup everything above this line