Commit 26c1972c authored by Savas Vedova's avatar Savas Vedova

Merge branch 'select-line-numbers' into 'master'

Ability to select line numbers + fix Code Intelligence

See merge request gitlab-org/gitlab!77229
parents 835763c2 af036e02
...@@ -12,31 +12,6 @@ export default { ...@@ -12,31 +12,6 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
currentlyHighlightedLine: null,
};
},
mounted() {
this.scrollToLine();
},
methods: {
scrollToLine(hash = window.location.hash) {
const lineToHighlight = hash && this.$el.querySelector(hash);
if (!lineToHighlight) {
return;
}
if (this.currentlyHighlightedLine) {
this.currentlyHighlightedLine.classList.remove('hll');
}
lineToHighlight.classList.add('hll');
this.currentlyHighlightedLine = lineToHighlight;
lineToHighlight.scrollIntoView({ behavior: 'smooth', block: 'center' });
},
},
}; };
</script> </script>
<template> <template>
...@@ -46,9 +21,9 @@ export default { ...@@ -46,9 +21,9 @@ export default {
:id="`L${line}`" :id="`L${line}`"
:key="line" :key="line"
class="diff-line-num" class="diff-line-num"
:href="`#L${line}`" :href="`#LC${line}`"
:data-line-number="line" :data-line-number="line"
@click="scrollToLine(`#L${line}`)" @click="$emit('select-line', `#LC${line}`)"
> >
<gl-icon :size="12" name="link" /> <gl-icon :size="12" name="link" />
{{ line }} {{ line }}
......
<script> <script>
import { GlSafeHtmlDirective } from '@gitlab/ui'; import { GlSafeHtmlDirective } from '@gitlab/ui';
import LineNumbers from '~/vue_shared/components/line_numbers.vue'; import LineNumbers from '~/vue_shared/components/line_numbers.vue';
import { sanitize } from '~/lib/dompurify';
const LINE_SELECT_CLASS_NAME = 'hll';
export default { export default {
components: { components: {
...@@ -46,7 +49,12 @@ export default { ...@@ -46,7 +49,12 @@ export default {
} }
} }
return highlightedContent; return this.wrapLines(highlightedContent);
},
},
watch: {
highlightedContent() {
this.$nextTick(() => this.selectLine());
}, },
}, },
async mounted() { async mounted() {
...@@ -73,16 +81,39 @@ export default { ...@@ -73,16 +81,39 @@ export default {
return languageDefinition; return languageDefinition;
}, },
wrapLines(content) {
return (
content &&
content
.split('\n')
.map((line, i) => `<span id="LC${i + 1}" class="line">${line}</span>`)
.join('\r\n')
);
},
selectLine(hash = sanitize(window.location.hash)) {
const lineToSelect = hash && this.$el.querySelector(hash);
if (!lineToSelect) {
return;
}
if (this.$options.currentlySelectedLine) {
this.$options.currentlySelectedLine.classList.remove(LINE_SELECT_CLASS_NAME);
}
lineToSelect.classList.add(LINE_SELECT_CLASS_NAME);
this.$options.currentlySelectedLine = lineToSelect;
lineToSelect.scrollIntoView({ behavior: 'smooth', block: 'center' });
},
}, },
userColorScheme: window.gon.user_color_scheme, userColorScheme: window.gon.user_color_scheme,
currentlySelectedLine: null,
}; };
</script> </script>
<template> <template>
<div class="file-content code" :class="$options.userColorScheme"> <div class="file-content code" :class="$options.userColorScheme">
<line-numbers :lines="lineNumbers" /> <line-numbers :lines="lineNumbers" @select-line="selectLine" />
<pre <pre class="code"><code v-safe-html="highlightedContent"></code>
class="code gl-pl-3!"
><code v-safe-html="highlightedContent" class="gl-white-space-pre-wrap!"></code>
</pre> </pre>
</div> </div>
</template> </template>
...@@ -13,7 +13,6 @@ describe('Line Numbers component', () => { ...@@ -13,7 +13,6 @@ describe('Line Numbers component', () => {
const findGlIcon = () => wrapper.findComponent(GlIcon); const findGlIcon = () => wrapper.findComponent(GlIcon);
const findLineNumbers = () => wrapper.findAllComponents(GlLink); const findLineNumbers = () => wrapper.findAllComponents(GlLink);
const findFirstLineNumber = () => findLineNumbers().at(0); const findFirstLineNumber = () => findLineNumbers().at(0);
const findSecondLineNumber = () => findLineNumbers().at(1);
beforeEach(() => createComponent()); beforeEach(() => createComponent());
...@@ -24,7 +23,7 @@ describe('Line Numbers component', () => { ...@@ -24,7 +23,7 @@ describe('Line Numbers component', () => {
expect(findLineNumbers().length).toBe(lines); expect(findLineNumbers().length).toBe(lines);
expect(findFirstLineNumber().attributes()).toMatchObject({ expect(findFirstLineNumber().attributes()).toMatchObject({
id: 'L1', id: 'L1',
href: '#L1', href: '#LC1',
}); });
}); });
...@@ -37,35 +36,13 @@ describe('Line Numbers component', () => { ...@@ -37,35 +36,13 @@ describe('Line Numbers component', () => {
}); });
describe('clicking a line number', () => { describe('clicking a line number', () => {
let firstLineNumber;
let firstLineNumberElement;
beforeEach(() => { beforeEach(() => {
firstLineNumber = findFirstLineNumber(); jest.spyOn(wrapper.vm, '$emit');
firstLineNumberElement = firstLineNumber.element; findFirstLineNumber().vm.$emit('click');
jest.spyOn(firstLineNumberElement, 'scrollIntoView');
jest.spyOn(firstLineNumberElement.classList, 'add');
jest.spyOn(firstLineNumberElement.classList, 'remove');
firstLineNumber.vm.$emit('click');
});
it('adds the highlight (hll) class', () => {
expect(firstLineNumberElement.classList.add).toHaveBeenCalledWith('hll');
}); });
it('removes the highlight (hll) class from a previously highlighted line', () => { it('emits a select-line event', () => {
findSecondLineNumber().vm.$emit('click'); expect(wrapper.vm.$emit).toHaveBeenCalledWith('select-line', '#LC1');
expect(firstLineNumberElement.classList.remove).toHaveBeenCalledWith('hll');
});
it('scrolls the line into view', () => {
expect(firstLineNumberElement.scrollIntoView).toHaveBeenCalledWith({
behavior: 'smooth',
block: 'center',
});
}); });
}); });
}); });
...@@ -9,7 +9,7 @@ jest.mock('highlight.js/lib/core'); ...@@ -9,7 +9,7 @@ jest.mock('highlight.js/lib/core');
describe('Source Viewer component', () => { describe('Source Viewer component', () => {
let wrapper; let wrapper;
const content = `// Some source code`; const content = `// Some source code`;
const highlightedContent = `<span data-testid='test-highlighted'>${content}</span>`; const highlightedContent = `<span data-testid='test-highlighted' id='LC1'>${content}</span><span id='LC2'></span>`;
const language = 'javascript'; const language = 'javascript';
hljs.highlight.mockImplementation(() => ({ value: highlightedContent })); hljs.highlight.mockImplementation(() => ({ value: highlightedContent }));
...@@ -22,6 +22,7 @@ describe('Source Viewer component', () => { ...@@ -22,6 +22,7 @@ describe('Source Viewer component', () => {
const findLineNumbers = () => wrapper.findComponent(LineNumbers); const findLineNumbers = () => wrapper.findComponent(LineNumbers);
const findHighlightedContent = () => wrapper.findByTestId('test-highlighted'); const findHighlightedContent = () => wrapper.findByTestId('test-highlighted');
const findFirstLine = () => wrapper.find('#LC1');
beforeEach(() => createComponent()); beforeEach(() => createComponent());
...@@ -56,4 +57,37 @@ describe('Source Viewer component', () => { ...@@ -56,4 +57,37 @@ describe('Source Viewer component', () => {
expect(findHighlightedContent().exists()).toBe(true); expect(findHighlightedContent().exists()).toBe(true);
}); });
}); });
describe('selecting a line', () => {
let firstLine;
let firstLineElement;
beforeEach(() => {
firstLine = findFirstLine();
firstLineElement = firstLine.element;
jest.spyOn(firstLineElement, 'scrollIntoView');
jest.spyOn(firstLineElement.classList, 'add');
jest.spyOn(firstLineElement.classList, 'remove');
findLineNumbers().vm.$emit('select-line', '#LC1');
});
it('adds the highlight (hll) class', () => {
expect(firstLineElement.classList.add).toHaveBeenCalledWith('hll');
});
it('removes the highlight (hll) class from a previously highlighted line', () => {
findLineNumbers().vm.$emit('select-line', '#LC2');
expect(firstLineElement.classList.remove).toHaveBeenCalledWith('hll');
});
it('scrolls the line into view', () => {
expect(firstLineElement.scrollIntoView).toHaveBeenCalledWith({
behavior: 'smooth',
block: 'center',
});
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment