diff options
Diffstat (limited to 'app/assets/javascripts/ide/components')
14 files changed, 206 insertions, 88 deletions
diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue index 183816921c1..69e5cd839b4 100644 --- a/app/assets/javascripts/ide/components/activity_bar.vue +++ b/app/assets/javascripts/ide/components/activity_bar.vue @@ -32,7 +32,7 @@ export default { </script> <template> - <nav class="ide-activity-bar"> + <nav class="ide-activity-bar" data-testid="left-sidebar"> <ul class="list-unstyled"> <li> <button diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue index de4b0a34002..b89329c92ec 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue @@ -1,8 +1,8 @@ <script> -/* eslint-disable vue/no-v-html */ import { escape } from 'lodash'; import { mapState, mapGetters, createNamespacedHelpers } from 'vuex'; -import { sprintf, s__ } from '~/locale'; +import { GlSprintf } from '@gitlab/ui'; +import { s__ } from '~/locale'; import consts from '../../stores/modules/commit/constants'; import RadioGroup from './radio_group.vue'; import NewMergeRequestOption from './new_merge_request_option.vue'; @@ -13,6 +13,7 @@ const { mapState: mapCommitState, mapActions: mapCommitActions } = createNamespa export default { components: { + GlSprintf, RadioGroup, NewMergeRequestOption, }, @@ -20,12 +21,8 @@ export default { ...mapState(['currentBranchId', 'changedFiles', 'stagedFiles']), ...mapCommitState(['commitAction']), ...mapGetters(['currentBranch', 'emptyRepo', 'canPushToBranch']), - commitToCurrentBranchText() { - return sprintf( - s__('IDE|Commit to %{branchName} branch'), - { branchName: `<strong class="monospace">${escape(this.currentBranchId)}</strong>` }, - false, - ); + currentBranchText() { + return escape(this.currentBranchId); }, containsStagedChanges() { return this.changedFiles.length > 0 && this.stagedFiles.length > 0; @@ -77,11 +74,13 @@ export default { :disabled="!canPushToBranch" :title="$options.currentBranchPermissionsTooltip" > - <span - class="ide-option-label" - data-qa-selector="commit_to_current_branch_radio" - v-html="commitToCurrentBranchText" - ></span> + <span class="ide-option-label" data-qa-selector="commit_to_current_branch_radio"> + <gl-sprintf :message="s__('IDE|Commit to %{branchName} branch')"> + <template #branchName> + <strong class="monospace">{{ currentBranchText }}</strong> + </template> + </gl-sprintf> + </span> </radio-group> <template v-if="!emptyRepo"> <radio-group diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue index 2787b10a48b..5b392470e41 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue @@ -1,5 +1,5 @@ <script> -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlPopover } from '@gitlab/ui'; import { __, sprintf } from '../../../locale'; import popover from '../../../vue_shared/directives/popover'; import { MAX_TITLE_LENGTH, MAX_BODY_LENGTH } from '../../constants'; @@ -10,6 +10,7 @@ export default { }, components: { GlIcon, + GlPopover, }, props: { text: { @@ -58,7 +59,7 @@ export default { }, }, popoverOptions: { - trigger: 'hover', + triggers: 'hover', placement: 'top', content: sprintf( __(` @@ -83,9 +84,16 @@ export default { <ul class="nav-links"> <li> {{ __('Commit Message') }} - <span v-popover="$options.popoverOptions" class="form-text text-muted gl-ml-3"> - <gl-icon name="question" /> - </span> + <div id="ide-commit-message-popover-container"> + <span id="ide-commit-message-question" class="form-text text-muted gl-ml-3"> + <gl-icon name="question" /> + </span> + <gl-popover + target="ide-commit-message-question" + container="ide-commit-message-popover-container" + v-bind="$options.popoverOptions" + /> + </div> </li> </ul> </div> diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue index 732fa0786b0..dec8aa61838 100644 --- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue +++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue @@ -1,8 +1,12 @@ <script> +import { GlButton } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; import { viewerTypes } from '../constants'; export default { + components: { + GlButton, + }, props: { viewer: { type: String, @@ -31,7 +35,7 @@ export default { <template> <div class="dropdown"> - <button type="button" class="btn btn-link" data-toggle="dropdown">{{ __('Edit') }}</button> + <gl-button variant="link" data-toggle="dropdown">{{ __('Edit') }}</gl-button> <div class="dropdown-menu dropdown-menu-selectable dropdown-open-left"> <ul> <li> diff --git a/app/assets/javascripts/ide/components/file_templates/dropdown.vue b/app/assets/javascripts/ide/components/file_templates/dropdown.vue index d80662f6ae1..cfd2555b769 100644 --- a/app/assets/javascripts/ide/components/file_templates/dropdown.vue +++ b/app/assets/javascripts/ide/components/file_templates/dropdown.vue @@ -1,12 +1,13 @@ <script> import $ from 'jquery'; import { mapActions, mapState } from 'vuex'; -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlIcon, GlLoadingIcon } from '@gitlab/ui'; import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; export default { components: { DropdownButton, + GlIcon, GlLoadingIcon, }, props: { @@ -85,7 +86,7 @@ export default { type="search" class="dropdown-input-field qa-dropdown-filter-input" /> - <i aria-hidden="true" class="fa fa-search dropdown-input-search"></i> + <gl-icon name="search" class="dropdown-input-search" aria-hidden="true" /> </div> <div class="dropdown-content"> <gl-loading-icon v-if="showLoading" size="lg" /> diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue index 1b03d9eee8b..b08497f8f82 100644 --- a/app/assets/javascripts/ide/components/ide.vue +++ b/app/assets/javascripts/ide/components/ide.vue @@ -2,7 +2,18 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import { __ } from '~/locale'; +import { + WEBIDE_MARK_APP_START, + WEBIDE_MARK_FILE_FINISH, + WEBIDE_MARK_FILE_CLICKED, + WEBIDE_MARK_TREE_FINISH, + WEBIDE_MEASURE_TREE_FROM_REQUEST, + WEBIDE_MEASURE_FILE_FROM_REQUEST, + WEBIDE_MEASURE_FILE_AFTER_INTERACTION, +} from '~/performance_constants'; +import { performanceMarkAndMeasure } from '~/performance_utils'; import { modalTypes } from '../constants'; +import eventHub from '../eventhub'; import FindFile from '~/vue_shared/components/file_finder/index.vue'; import NewModal from './new_dropdown/modal.vue'; import IdeSidebar from './ide_side_bar.vue'; @@ -14,6 +25,50 @@ import ErrorMessage from './error_message.vue'; import CommitEditorHeader from './commit_sidebar/editor_header.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +const markPerformance = params => { + performanceMarkAndMeasure(params); +}; +const markTreePerformance = () => { + markPerformance({ + mark: WEBIDE_MARK_TREE_FINISH, + measures: [ + { + name: WEBIDE_MEASURE_TREE_FROM_REQUEST, + start: undefined, + end: WEBIDE_MARK_TREE_FINISH, + }, + ], + }); +}; +const markEditorLoadPerformance = () => { + markPerformance({ + mark: WEBIDE_MARK_FILE_FINISH, + measures: [ + { + name: WEBIDE_MEASURE_FILE_FROM_REQUEST, + start: undefined, + end: WEBIDE_MARK_FILE_FINISH, + }, + ], + }); +}; +const markEditorInteractionPerformance = () => { + markPerformance({ + mark: WEBIDE_MARK_FILE_FINISH, + measures: [ + { + name: WEBIDE_MEASURE_FILE_AFTER_INTERACTION, + start: WEBIDE_MARK_FILE_CLICKED, + end: WEBIDE_MARK_FILE_FINISH, + }, + ], + }); +}; + +eventHub.$on(WEBIDE_MEASURE_TREE_FROM_REQUEST, markTreePerformance); +eventHub.$on(WEBIDE_MEASURE_FILE_FROM_REQUEST, markEditorLoadPerformance); +eventHub.$on(WEBIDE_MEASURE_FILE_AFTER_INTERACTION, markEditorInteractionPerformance); + export default { components: { NewModal, @@ -59,6 +114,9 @@ export default { if (this.themeName) document.querySelector('.navbar-gitlab').classList.add(`theme-${this.themeName}`); }, + beforeCreate() { + performance.mark(WEBIDE_MARK_APP_START); + }, methods: { ...mapActions(['toggleFileFinder']), onBeforeUnload(e = {}) { diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue index e36d0a5a5b1..7d2f0acb08c 100644 --- a/app/assets/javascripts/ide/components/ide_review.vue +++ b/app/assets/javascripts/ide/components/ide_review.vue @@ -23,26 +23,32 @@ export default { }, }, mounted() { - if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) { - this.$router.push(this.getUrlForPath(this.activeFile.path), () => { - this.updateViewer('editor'); - }); - } else if (this.activeFile && this.activeFile.deleted) { - this.resetOpenFiles(); - } - - this.$nextTick(() => { - this.updateViewer(this.currentMergeRequestId ? viewerTypes.mr : viewerTypes.diff); - }); + this.initialize(); + }, + activated() { + this.initialize(); }, methods: { ...mapActions(['updateViewer', 'resetOpenFiles']), + initialize() { + if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) { + this.$router.push(this.getUrlForPath(this.activeFile.path), () => { + this.updateViewer(viewerTypes.edit); + }); + } else if (this.activeFile && this.activeFile.deleted) { + this.resetOpenFiles(); + } + + this.$nextTick(() => { + this.updateViewer(this.currentMergeRequestId ? viewerTypes.mr : viewerTypes.diff); + }); + }, }, }; </script> <template> - <ide-tree-list :viewer-type="viewer" header-class="ide-review-header"> + <ide-tree-list header-class="ide-review-header"> <template #header> <div class="ide-review-button-holder"> {{ __('Review') }} diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue index ed68ca5cae9..53dfc133fc8 100644 --- a/app/assets/javascripts/ide/components/ide_side_bar.vue +++ b/app/assets/javascripts/ide/components/ide_side_bar.vue @@ -7,9 +7,8 @@ import ActivityBar from './activity_bar.vue'; import RepoCommitSection from './repo_commit_section.vue'; import CommitForm from './commit_sidebar/form.vue'; import IdeReview from './ide_review.vue'; -import SuccessMessage from './commit_sidebar/success_message.vue'; import IdeProjectHeader from './ide_project_header.vue'; -import { leftSidebarViews, SIDEBAR_INIT_WIDTH } from '../constants'; +import { SIDEBAR_INIT_WIDTH } from '../constants'; export default { components: { @@ -20,18 +19,11 @@ export default { IdeTree, CommitForm, IdeReview, - SuccessMessage, IdeProjectHeader, }, computed: { ...mapState(['loading', 'currentActivityView', 'changedFiles', 'stagedFiles', 'lastCommitMsg']), ...mapGetters(['currentProject', 'someUncommittedChanges']), - showSuccessMessage() { - return ( - this.currentActivityView === leftSidebarViews.edit.name && - (this.lastCommitMsg && !this.someUncommittedChanges) - ); - }, }, SIDEBAR_INIT_WIDTH, }; @@ -44,7 +36,7 @@ export default { class="multi-file-commit-panel flex-column" > <template v-if="loading"> - <div class="multi-file-commit-panel-inner"> + <div class="multi-file-commit-panel-inner" data-testid="ide-side-bar-inner"> <div v-for="n in 3" :key="n" class="multi-file-loading-container"> <gl-skeleton-loading /> </div> @@ -54,9 +46,11 @@ export default { <ide-project-header :project="currentProject" /> <div class="ide-context-body d-flex flex-fill"> <activity-bar /> - <div class="multi-file-commit-panel-inner"> + <div class="multi-file-commit-panel-inner" data-testid="ide-side-bar-inner"> <div class="multi-file-commit-panel-inner-content"> - <component :is="currentActivityView" /> + <keep-alive> + <component :is="currentActivityView" /> + </keep-alive> </div> <commit-form /> </div> diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue index 747d5044790..51d783df0ad 100644 --- a/app/assets/javascripts/ide/components/ide_tree.vue +++ b/app/assets/javascripts/ide/components/ide_tree.vue @@ -1,6 +1,6 @@ <script> import { mapState, mapGetters, mapActions } from 'vuex'; -import { modalTypes } from '../constants'; +import { modalTypes, viewerTypes } from '../constants'; import IdeTreeList from './ide_tree_list.vue'; import Upload from './new_dropdown/upload.vue'; import NewEntryButton from './new_dropdown/button.vue'; @@ -18,15 +18,10 @@ export default { ...mapGetters(['currentProject', 'currentTree', 'activeFile', 'getUrlForPath']), }, mounted() { - if (!this.activeFile) return; - - if (this.activeFile.pending && !this.activeFile.deleted) { - this.$router.push(this.getUrlForPath(this.activeFile.path), () => { - this.updateViewer('editor'); - }); - } else if (this.activeFile.deleted) { - this.resetOpenFiles(); - } + this.initialize(); + }, + activated() { + this.initialize(); }, methods: { ...mapActions(['updateViewer', 'createTempEntry', 'resetOpenFiles']), @@ -36,12 +31,27 @@ export default { createNewFolder() { this.$refs.newModal.open(modalTypes.tree); }, + initialize() { + this.$nextTick(() => { + this.updateViewer(viewerTypes.edit); + }); + + if (!this.activeFile) return; + + if (this.activeFile.pending && !this.activeFile.deleted) { + this.$router.push(this.getUrlForPath(this.activeFile.path), () => { + this.updateViewer(viewerTypes.edit); + }); + } else if (this.activeFile.deleted) { + this.resetOpenFiles(); + } + }, }, }; </script> <template> - <ide-tree-list viewer-type="editor"> + <ide-tree-list> <template #header> {{ __('Edit') }} <div class="ide-tree-actions ml-auto d-flex"> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index 776d8459515..bbec20776bf 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -2,6 +2,13 @@ import { mapActions, mapGetters, mapState } from 'vuex'; import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui'; import FileTree from '~/vue_shared/components/file_tree.vue'; +import { + WEBIDE_MARK_TREE_START, + WEBIDE_MEASURE_TREE_FROM_REQUEST, + WEBIDE_MARK_FILE_CLICKED, +} from '~/performance_constants'; +import { performanceMarkAndMeasure } from '~/performance_utils'; +import eventHub from '../eventhub'; import IdeFileRow from './ide_file_row.vue'; import NavDropdown from './nav_dropdown.vue'; @@ -12,10 +19,6 @@ export default { FileTree, }, props: { - viewerType: { - type: String, - required: true, - }, headerClass: { type: String, required: false, @@ -29,11 +32,21 @@ export default { return !this.currentTree || this.currentTree.loading; }, }, - mounted() { - this.updateViewer(this.viewerType); + beforeCreate() { + performanceMarkAndMeasure({ mark: WEBIDE_MARK_TREE_START }); + }, + updated() { + if (this.currentTree?.tree?.length) { + this.$nextTick(() => { + eventHub.$emit(WEBIDE_MEASURE_TREE_FROM_REQUEST); + }); + } }, methods: { - ...mapActions(['updateViewer', 'toggleTreeOpen']), + ...mapActions(['toggleTreeOpen']), + clickedFile() { + performanceMarkAndMeasure({ mark: WEBIDE_MARK_FILE_CLICKED }); + }, }, IdeFileRow, }; @@ -51,7 +64,7 @@ export default { <nav-dropdown /> <slot name="header"></slot> </header> - <div class="ide-tree-body h-100"> + <div class="ide-tree-body h-100" data-testid="ide-tree-body"> <template v-if="currentTree.tree.length"> <file-tree v-for="file in currentTree.tree" @@ -60,6 +73,7 @@ export default { :level="0" :file-row-component="$options.IdeFileRow" @toggleTreeOpen="toggleTreeOpen" + @clickFile="clickedFile" /> </template> <div v-else class="file-row">{{ __('No files') }}</div> diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue index 11033a5cc88..394a512f5bd 100644 --- a/app/assets/javascripts/ide/components/jobs/detail.vue +++ b/app/assets/javascripts/ide/components/jobs/detail.vue @@ -2,9 +2,8 @@ /* eslint-disable vue/no-v-html */ import { mapActions, mapState } from 'vuex'; import { throttle } from 'lodash'; -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlTooltipDirective } from '@gitlab/ui'; import { __ } from '../../../locale'; -import tooltip from '../../../vue_shared/directives/tooltip'; import ScrollButton from './detail/scroll_button.vue'; import JobDescription from './detail/description.vue'; @@ -15,7 +14,7 @@ const scrollPositions = { export default { directives: { - tooltip, + GlTooltip: GlTooltipDirective, }, components: { GlIcon, @@ -84,7 +83,7 @@ export default { <job-description :job="detailJob" /> <div class="controllers ml-auto"> <a - v-tooltip + v-gl-tooltip :title="__('Show complete raw log')" :href="detailJob.rawPath" data-placement="top" diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue index 528475849de..5ad836f346a 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue @@ -152,6 +152,7 @@ export default { v-model.trim="entryName" type="text" class="form-control" + data-testid="file-name-field" data-qa-selector="file_name_field" :placeholder="placeholder" /> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 5eed57bb6c5..92b99b5c731 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -26,28 +26,34 @@ export default { }, }, mounted() { - const file = - this.lastOpenedFile && this.lastOpenedFile.type !== 'tree' - ? this.lastOpenedFile - : this.activeFile; - - if (!file) return; - - this.openPendingTab({ - file, - keyPrefix: file.staged ? stageKeys.staged : stageKeys.unstaged, - }) - .then(changeViewer => { - if (changeViewer) { - this.updateViewer('diff'); - } - }) - .catch(e => { - throw e; - }); + this.initialize(); + }, + activated() { + this.initialize(); }, methods: { ...mapActions(['openPendingTab', 'updateViewer', 'updateActivityBarView']), + initialize() { + const file = + this.lastOpenedFile && this.lastOpenedFile.type !== 'tree' + ? this.lastOpenedFile + : this.activeFile; + + if (!file) return; + + this.openPendingTab({ + file, + keyPrefix: file.staged ? stageKeys.staged : stageKeys.unstaged, + }) + .then(changeViewer => { + if (changeViewer) { + this.updateViewer('diff'); + } + }) + .catch(e => { + throw e; + }); + }, }, stageKeys, }; diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index f342ce1739c..7465772d86a 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -5,6 +5,14 @@ import { deprecatedCreateFlash as flash } from '~/flash'; import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; import { + WEBIDE_MARK_FILE_CLICKED, + WEBIDE_MARK_FILE_START, + WEBIDE_MEASURE_FILE_AFTER_INTERACTION, + WEBIDE_MEASURE_FILE_FROM_REQUEST, +} from '~/performance_constants'; +import { performanceMarkAndMeasure } from '~/performance_utils'; +import eventHub from '../eventhub'; +import { leftSidebarViews, viewerTypes, FILE_VIEW_MODE_EDITOR, @@ -164,6 +172,9 @@ export default { } }, }, + beforeCreate() { + performanceMarkAndMeasure({ mark: WEBIDE_MARK_FILE_START }); + }, beforeDestroy() { this.editor.dispose(); }, @@ -289,6 +300,13 @@ export default { }); this.$emit('editorSetup'); + this.$nextTick(() => { + if (performance.getEntriesByName(WEBIDE_MARK_FILE_CLICKED).length) { + eventHub.$emit(WEBIDE_MEASURE_FILE_AFTER_INTERACTION); + } else { + eventHub.$emit(WEBIDE_MEASURE_FILE_FROM_REQUEST); + } + }); }, refreshEditorDimensions() { if (this.showEditor) { |