diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-08 21:08:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-04-08 21:08:29 +0300 |
commit | 842ac3526cba09feb4b9ccefd0aeeb6edc02035d (patch) | |
tree | 2b73dca2be277685aefdc4f775c211049b4dae7c /app/assets/javascripts | |
parent | 9f9dc2bc412632e6b459d0bb9e1ac205c8cf34af (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
15 files changed, 82 insertions, 10 deletions
diff --git a/app/assets/javascripts/code_navigation/components/app.vue b/app/assets/javascripts/code_navigation/components/app.vue index d65b9a71288..81edbb4182e 100644 --- a/app/assets/javascripts/code_navigation/components/app.vue +++ b/app/assets/javascripts/code_navigation/components/app.vue @@ -23,6 +23,11 @@ export default { required: false, default: null, }, + wrapTextNodes: { + type: Boolean, + required: false, + default: false, + }, }, computed: { ...mapState([ @@ -37,6 +42,7 @@ export default { const initialData = { blobs: [{ path: this.blobPath, codeNavigationPath: this.codeNavigationPath }], definitionPathPrefix: this.pathPrefix, + wrapTextNodes: this.wrapTextNodes, }; this.setInitialData(initialData); } diff --git a/app/assets/javascripts/code_navigation/store/actions.js b/app/assets/javascripts/code_navigation/store/actions.js index 0b6b8437db5..562b78a891a 100644 --- a/app/assets/javascripts/code_navigation/store/actions.js +++ b/app/assets/javascripts/code_navigation/store/actions.js @@ -22,7 +22,7 @@ export default { ...d, definitionLineNumber: parseInt(d.definition_path?.split('#L').pop() || 0, 10), }; - addInteractionClass(path, d); + addInteractionClass({ path, d, wrapTextNodes: state.wrapTextNodes }); } return acc; }, {}); @@ -34,7 +34,9 @@ export default { }, showBlobInteractionZones({ state }, path) { if (state.data && state.data[path]) { - Object.values(state.data[path]).forEach((d) => addInteractionClass(path, d)); + Object.values(state.data[path]).forEach((d) => + addInteractionClass({ path, d, wrapTextNodes: state.wrapTextNodes }), + ); } }, showDefinition({ commit, state }, { target: el }) { diff --git a/app/assets/javascripts/code_navigation/store/mutations.js b/app/assets/javascripts/code_navigation/store/mutations.js index 07b190c7476..98beffe231c 100644 --- a/app/assets/javascripts/code_navigation/store/mutations.js +++ b/app/assets/javascripts/code_navigation/store/mutations.js @@ -1,9 +1,10 @@ import * as types from './mutation_types'; export default { - [types.SET_INITIAL_DATA](state, { blobs, definitionPathPrefix }) { + [types.SET_INITIAL_DATA](state, { blobs, definitionPathPrefix, wrapTextNodes }) { state.blobs = blobs; state.definitionPathPrefix = definitionPathPrefix; + state.wrapTextNodes = wrapTextNodes; }, [types.REQUEST_DATA](state) { state.loading = true; diff --git a/app/assets/javascripts/code_navigation/store/state.js b/app/assets/javascripts/code_navigation/store/state.js index 569d2f7b319..17505b8392c 100644 --- a/app/assets/javascripts/code_navigation/store/state.js +++ b/app/assets/javascripts/code_navigation/store/state.js @@ -2,6 +2,7 @@ export default () => ({ blobs: [], loading: false, data: null, + wrapTextNodes: false, currentDefinition: null, currentDefinitionPosition: null, currentBlobPath: null, diff --git a/app/assets/javascripts/code_navigation/utils/dom_utils.js b/app/assets/javascripts/code_navigation/utils/dom_utils.js new file mode 100644 index 00000000000..1a65c1a64a2 --- /dev/null +++ b/app/assets/javascripts/code_navigation/utils/dom_utils.js @@ -0,0 +1,31 @@ +const TEXT_NODE = 3; + +const isTextNode = ({ nodeType }) => nodeType === TEXT_NODE; + +const isBlank = (str) => !str || /^\s*$/.test(str); + +const isMatch = (s1, s2) => !isBlank(s1) && s1.trim() === s2.trim(); + +const createSpan = (content) => { + const span = document.createElement('span'); + span.innerText = content; + return span; +}; + +const wrapSpacesWithSpans = (text) => text.replace(/ /g, createSpan(' ').outerHTML); + +const wrapTextWithSpan = (el, text) => { + if (isTextNode(el) && isMatch(el.textContent, text)) { + const newEl = createSpan(text.trim()); + el.replaceWith(newEl); + } +}; + +const wrapNodes = (text) => { + const wrapper = createSpan(); + wrapper.innerHTML = wrapSpacesWithSpans(text); + wrapper.childNodes.forEach((el) => wrapTextWithSpan(el, text)); + return wrapper.childNodes; +}; + +export { wrapNodes, isTextNode }; diff --git a/app/assets/javascripts/code_navigation/utils/index.js b/app/assets/javascripts/code_navigation/utils/index.js index 6c078891ed4..0d72153d8fe 100644 --- a/app/assets/javascripts/code_navigation/utils/index.js +++ b/app/assets/javascripts/code_navigation/utils/index.js @@ -1,9 +1,11 @@ +import { wrapNodes, isTextNode } from './dom_utils'; + export const cachedData = new Map(); export const getCurrentHoverElement = () => cachedData.get('current'); export const setCurrentHoverElement = (el) => cachedData.set('current', el); -export const addInteractionClass = (path, d) => { +export const addInteractionClass = ({ path, d, wrapTextNodes }) => { const lineNumber = d.start_line + 1; const lines = document .querySelector(`[data-path="${path}"]`) @@ -12,13 +14,24 @@ export const addInteractionClass = (path, d) => { lines.forEach((line) => { let charCount = 0; + + if (wrapTextNodes) { + line.childNodes.forEach((elm) => { + if (isTextNode(elm)) { + // Highlight.js does not wrap all text nodes by default + // We need all text nodes to be wrapped in order to append code nav attributes + elm.replaceWith(...wrapNodes(elm.textContent)); + } + }); + } + const el = [...line.childNodes].find(({ textContent }) => { if (charCount === d.start_char) return true; charCount += textContent.length; return false; }); - if (el) { + if (el && !isTextNode(el)) { el.setAttribute('data-char-index', d.start_char); el.setAttribute('data-line-index', d.start_line); el.classList.add('cursor-pointer', 'code-navigation', 'js-code-navigation'); diff --git a/app/assets/javascripts/incidents/components/incidents_list.vue b/app/assets/javascripts/incidents/components/incidents_list.vue index b55cd406adc..9e0c31e29e5 100644 --- a/app/assets/javascripts/incidents/components/incidents_list.vue +++ b/app/assets/javascripts/incidents/components/incidents_list.vue @@ -358,7 +358,7 @@ export default { :loading="redirecting" :disabled="redirecting" category="primary" - variant="success" + variant="confirm" :href="newIncidentPath" @click="navigateToCreateNewIncident" > diff --git a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue index 753a15871ab..f16e0287d5d 100644 --- a/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue +++ b/app/assets/javascripts/jobs/components/table/cells/actions_cell.vue @@ -171,6 +171,7 @@ export default { data-testid="cancel-button" icon="cancel" :title="$options.CANCEL" + :aria-label="$options.CANCEL" :disabled="cancelBtnDisabled" @click="cancelJob()" /> @@ -182,6 +183,7 @@ export default { v-gl-modal-directive="$options.playJobModalId" icon="play" :title="$options.ACTIONS_START_NOW" + :aria-label="$options.ACTIONS_START_NOW" data-testid="play-scheduled" /> <gl-modal @@ -196,6 +198,7 @@ export default { <gl-button icon="time-out" :title="$options.ACTIONS_UNSCHEDULE" + :aria-label="$options.ACTIONS_UNSCHEDULE" :disabled="unscheduleBtnDisabled" data-testid="unschedule" @click="unscheduleJob()" @@ -207,6 +210,7 @@ export default { v-if="manualJobPlayable" icon="play" :title="$options.ACTIONS_PLAY" + :aria-label="$options.ACTIONS_PLAY" :disabled="playManualBtnDisabled" data-testid="play" @click="playJob()" @@ -215,6 +219,7 @@ export default { v-else-if="isRetryable" icon="repeat" :title="$options.ACTIONS_RETRY" + :aria-label="$options.ACTIONS_RETRY" :method="currentJobMethod" :disabled="retryBtnDisabled" data-testid="retry" @@ -226,6 +231,7 @@ export default { v-if="shouldDisplayArtifacts" icon="download" :title="$options.ACTIONS_DOWNLOAD_ARTIFACTS" + :aria-label="$options.ACTIONS_DOWNLOAD_ARTIFACTS" :href="artifactDownloadPath" rel="nofollow" download diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue index a7fb322174b..1d8eb73d3d7 100644 --- a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue +++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_modal.vue @@ -66,6 +66,7 @@ export default { text: this.secondaryText, attributes: { variant: this.secondaryVariant, + category: 'secondary', }, }; }, diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index eec6b9ba542..a2fbb242222 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -357,7 +357,13 @@ export default { }) { if (shouldConfirm && isDirty) { const msg = __('Are you sure you want to cancel editing this comment?'); - const confirmed = await confirmAction(msg); + const confirmed = await confirmAction(msg, { + primaryBtnText: __('Cancel editing'), + primaryBtnVariant: 'danger', + secondaryBtnVariant: 'default', + secondaryBtnText: __('Continue editing'), + hideCancel: true, + }); if (!confirmed) return; } this.$refs.noteBody.resetAutoSave(); diff --git a/app/assets/javascripts/releases/components/app_edit_new.vue b/app/assets/javascripts/releases/components/app_edit_new.vue index 39140216bc5..8365e6a5ab0 100644 --- a/app/assets/javascripts/releases/components/app_edit_new.vue +++ b/app/assets/javascripts/releases/components/app_edit_new.vue @@ -185,7 +185,7 @@ export default { <gl-button class="mr-auto js-no-auto-disable" category="primary" - variant="success" + variant="confirm" type="submit" :disabled="isFormSubmissionDisabled" data-testid="submit-button" diff --git a/app/assets/javascripts/releases/components/app_index.vue b/app/assets/javascripts/releases/components/app_index.vue index 5bc855d0c18..59fa2fca736 100644 --- a/app/assets/javascripts/releases/components/app_index.vue +++ b/app/assets/javascripts/releases/components/app_index.vue @@ -238,7 +238,7 @@ export default { :href="newReleasePath" :aria-describedby="shouldRenderEmptyState && 'releases-description'" category="primary" - variant="success" + variant="confirm" >{{ $options.i18n.newRelease }}</gl-button > </div> diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index 85652301f4d..7aadc00ccea 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -301,6 +301,7 @@ export default { :code-navigation-path="blobInfo.codeNavigationPath" :blob-path="blobInfo.path" :path-prefix="blobInfo.projectBlobPathRoot" + :wrap-text-nodes="glFeatures.highlightJs" /> </div> </div> diff --git a/app/assets/javascripts/user_lists/components/user_list_form.vue b/app/assets/javascripts/user_lists/components/user_list_form.vue index b53aaf46ace..44aa2d9a5b4 100644 --- a/app/assets/javascripts/user_lists/components/user_list_form.vue +++ b/app/assets/javascripts/user_lists/components/user_list_form.vue @@ -84,7 +84,7 @@ export default { <gl-form-input id="user-list-name" v-model="name" data-testid="user-list-name" required /> </gl-form-group> <div :class="$options.classes.actionContainer"> - <gl-button variant="success" data-testid="save-user-list" @click="submit"> + <gl-button variant="confirm" data-testid="save-user-list" @click="submit"> {{ saveButtonLabel }} </gl-button> <gl-button :href="cancelPath" data-testid="user-list-cancel"> diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue index 15411e8d17e..edf2229a9a1 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue @@ -1,6 +1,7 @@ <script> import { GlSafeHtmlDirective, GlLoadingIcon } from '@gitlab/ui'; import LineHighlighter from '~/blob/line_highlighter'; +import eventHub from '~/notes/event_hub'; import { ROUGE_TO_HLJS_LANGUAGE_MAP, LINES_PER_CHUNK } from './constants'; import Chunk from './components/chunk.vue'; @@ -102,6 +103,8 @@ export default { Object.assign(chunk, { language, content: highlightedContent, isHighlighted: true }); this.selectLine(); + + this.$nextTick(() => eventHub.$emit('showBlobInteractionZones', this.blob.path)); }, highlight(content, language) { let detectedLanguage = language; @@ -153,6 +156,7 @@ export default { class="file-content code js-syntax-highlight blob-content gl-display-flex gl-flex-direction-column gl-overflow-auto" :class="$options.userColorScheme" data-type="simple" + :data-path="blob.path" data-qa-selector="blob_viewer_file_content" > <chunk |