Image Diff Viewing + Download Diff Viewing
This commit is contained in:
parent
929f12b61a
commit
8a23bcc9bc
|
|
@ -2,6 +2,7 @@
|
|||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import 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 { activityBarViews, viewerTypes } from '../constants';
|
||||
import Editor from '../lib/editor';
|
||||
import ExternalLink from './external_link.vue';
|
||||
|
|
@ -9,6 +10,7 @@ import ExternalLink from './external_link.vue';
|
|||
export default {
|
||||
components: {
|
||||
ContentViewer,
|
||||
DiffViewer,
|
||||
ExternalLink,
|
||||
},
|
||||
props: {
|
||||
|
|
@ -29,9 +31,18 @@ export default {
|
|||
shouldHideEditor() {
|
||||
return this.file && this.file.binary && !this.file.content;
|
||||
},
|
||||
showContentViewer() {
|
||||
return (
|
||||
(this.shouldHideEditor || this.file.viewMode === 'preview') &&
|
||||
(this.viewer !== viewerTypes.mr || !this.file.mrChange)
|
||||
);
|
||||
},
|
||||
showDiffViewer() {
|
||||
return this.shouldHideEditor && this.file.mrChange && this.viewer === viewerTypes.mr;
|
||||
},
|
||||
editTabCSS() {
|
||||
return {
|
||||
active: this.file.viewMode === 'edit',
|
||||
active: this.file.viewMode === 'editor',
|
||||
};
|
||||
},
|
||||
previewTabCSS() {
|
||||
|
|
@ -53,7 +64,7 @@ export default {
|
|||
if (this.currentActivityView !== activityBarViews.edit) {
|
||||
this.setFileViewMode({
|
||||
file: this.file,
|
||||
viewMode: 'edit',
|
||||
viewMode: 'editor',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +73,7 @@ export default {
|
|||
if (this.currentActivityView !== activityBarViews.edit) {
|
||||
this.setFileViewMode({
|
||||
file: this.file,
|
||||
viewMode: 'edit',
|
||||
viewMode: 'editor',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
@ -197,7 +208,7 @@ export default {
|
|||
<a
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
@click.prevent="setFileViewMode({ file, viewMode: 'edit' })">
|
||||
@click.prevent="setFileViewMode({ file, viewMode: 'editor' })">
|
||||
<template v-if="viewer === $options.viewerTypes.edit">
|
||||
{{ __('Edit') }}
|
||||
</template>
|
||||
|
|
@ -222,7 +233,7 @@ export default {
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="!shouldHideEditor && file.viewMode === 'edit'"
|
||||
v-show="!shouldHideEditor && file.viewMode ==='editor'"
|
||||
ref="editor"
|
||||
class="multi-file-editor-holder"
|
||||
:class="{
|
||||
|
|
@ -231,10 +242,18 @@ export default {
|
|||
>
|
||||
</div>
|
||||
<content-viewer
|
||||
v-if="shouldHideEditor || file.viewMode === 'preview'"
|
||||
v-if="showContentViewer"
|
||||
:content="file.content || file.raw"
|
||||
:path="file.rawPath || file.path"
|
||||
:file-size="file.size"
|
||||
:project-path="file.projectId"/>
|
||||
<diff-viewer
|
||||
v-if="showDiffViewer"
|
||||
:diff-mode="file.mrChange.diffMode"
|
||||
:new-path="file.mrChange.new_path"
|
||||
:new-sha="currentMergeRequest.sha"
|
||||
:old-path="file.mrChange.old_path"
|
||||
:old-sha="currentMergeRequest.baseCommitSha"
|
||||
:project-path="file.projectId"/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@ export const viewerTypes = {
|
|||
diff: 'diff',
|
||||
};
|
||||
|
||||
export const diffModes = {
|
||||
replaced: 'replaced',
|
||||
new: 'new',
|
||||
deleted: 'deleted',
|
||||
renamed: 'renamed',
|
||||
};
|
||||
|
||||
export const rightSidebarViews = {
|
||||
pipelines: 'pipelines-list',
|
||||
jobsDetail: 'jobs-detail',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
import * as types from '../mutation_types';
|
||||
import { diffModes } from '../../constants';
|
||||
|
||||
export default {
|
||||
[types.SET_FILE_ACTIVE](state, { path, active }) {
|
||||
|
|
@ -85,8 +86,19 @@ export default {
|
|||
});
|
||||
},
|
||||
[types.SET_FILE_MERGE_REQUEST_CHANGE](state, { file, mrChange }) {
|
||||
let diffMode = diffModes.replaced;
|
||||
if (mrChange.new_file) {
|
||||
diffMode = diffModes.new;
|
||||
} else if (mrChange.deleted_file) {
|
||||
diffMode = diffModes.deleted;
|
||||
} else if (mrChange.renamed_file) {
|
||||
diffMode = diffModes.renamed;
|
||||
}
|
||||
Object.assign(state.entries[file.path], {
|
||||
mrChange,
|
||||
mrChange: {
|
||||
...mrChange,
|
||||
diffMode,
|
||||
},
|
||||
});
|
||||
},
|
||||
[types.SET_FILE_VIEWMODE](state, { file, viewMode }) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export const dataStructure = () => ({
|
|||
editorColumn: 1,
|
||||
fileLanguage: '',
|
||||
eol: '',
|
||||
viewMode: 'edit',
|
||||
viewMode: 'editor',
|
||||
previewMode: null,
|
||||
size: 0,
|
||||
parentPath: null,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ export default {
|
|||
<div class="file-container">
|
||||
<div class="file-content">
|
||||
<p class="prepend-top-10 file-info">
|
||||
{{ fileName }} ({{ fileSizeReadable }})
|
||||
{{ fileName }}
|
||||
<template v-if="fileSize > 0">
|
||||
({{ fileSizeReadable }})
|
||||
</template>
|
||||
</p>
|
||||
<a
|
||||
:href="path"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import { numberToHumanSize } from '../../../../lib/utils/number_utils';
|
||||
|
||||
export default {
|
||||
|
|
@ -12,6 +13,10 @@ export default {
|
|||
required: false,
|
||||
default: 0,
|
||||
},
|
||||
renderInfo: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -26,14 +31,34 @@ export default {
|
|||
return numberToHumanSize(this.fileSize);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.resizeThrottled, false);
|
||||
},
|
||||
mounted() {
|
||||
// The onImgLoad may have happened before the control was actually mounted
|
||||
this.onImgLoad();
|
||||
this.resizeThrottled = _.throttle(this.onImgLoad, 400);
|
||||
window.addEventListener('resize', this.resizeThrottled, false);
|
||||
},
|
||||
methods: {
|
||||
onImgLoad() {
|
||||
const contentImg = this.$refs.contentImg;
|
||||
this.isZoomable =
|
||||
contentImg.naturalWidth > contentImg.width || contentImg.naturalHeight > contentImg.height;
|
||||
|
||||
this.width = contentImg.naturalWidth;
|
||||
this.height = contentImg.naturalHeight;
|
||||
if (contentImg) {
|
||||
this.isZoomable =
|
||||
contentImg.naturalWidth > contentImg.width ||
|
||||
contentImg.naturalHeight > contentImg.height;
|
||||
|
||||
this.width = contentImg.naturalWidth;
|
||||
this.height = contentImg.naturalHeight;
|
||||
|
||||
this.$emit('imgLoaded', {
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
renderedWidth: contentImg.clientWidth,
|
||||
renderedHeight: contentImg.clientHeight,
|
||||
});
|
||||
}
|
||||
},
|
||||
onImgClick() {
|
||||
if (this.isZoomable) this.isZoomed = !this.isZoomed;
|
||||
|
|
@ -47,20 +72,22 @@ export default {
|
|||
<div class="file-content image_file">
|
||||
<img
|
||||
ref="contentImg"
|
||||
:class="{ 'isZoomable': isZoomable, 'isZoomed': isZoomed }"
|
||||
:class="{ 'is-zoomable': isZoomable, 'is-zoomed': isZoomed }"
|
||||
:src="path"
|
||||
:alt="path"
|
||||
@load="onImgLoad"
|
||||
@click="onImgClick"/>
|
||||
<p class="file-info prepend-top-10">
|
||||
<p
|
||||
v-if="renderInfo"
|
||||
class="file-info prepend-top-10">
|
||||
<template v-if="fileSize>0">
|
||||
{{ fileSizeReadable }}
|
||||
</template>
|
||||
<template v-if="fileSize>0 && width && height">
|
||||
-
|
||||
|
|
||||
</template>
|
||||
<template v-if="width && height">
|
||||
{{ width }} x {{ height }}
|
||||
W: {{ width }} | H: {{ height }}
|
||||
</template>
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
export const diffModes = {
|
||||
replaced: 'replaced',
|
||||
new: 'new',
|
||||
deleted: 'deleted',
|
||||
renamed: 'renamed',
|
||||
};
|
||||
|
||||
export const imageViewMode = {
|
||||
twoup: 'twoup',
|
||||
swipe: 'swipe',
|
||||
onion: 'onion',
|
||||
};
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<script>
|
||||
import { viewerInformationForPath } from '../content_viewer/lib/viewer_utils';
|
||||
import ImageDiffViewer from './viewers/image_diff_viewer.vue';
|
||||
import DownloadDiffViewer from './viewers/download_diff_viewer.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
diffMode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
newPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
newSha: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldSha: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
viewer() {
|
||||
if (!this.newPath) return null;
|
||||
|
||||
const previewInfo = viewerInformationForPath(this.newPath);
|
||||
if (!previewInfo) return DownloadDiffViewer;
|
||||
|
||||
switch (previewInfo.id) {
|
||||
case 'image':
|
||||
return ImageDiffViewer;
|
||||
default:
|
||||
return DownloadDiffViewer;
|
||||
}
|
||||
},
|
||||
fullOldPath() {
|
||||
return `${gon.relative_url_root}/${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`;
|
||||
},
|
||||
fullNewPath() {
|
||||
return `${gon.relative_url_root}/${this.projectPath}/raw/${this.newSha}/${this.newPath}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="diff-file preview-container"
|
||||
v-if="viewer">
|
||||
<component
|
||||
:is="viewer"
|
||||
:diff-mode="diffMode"
|
||||
:new-path="fullNewPath"
|
||||
:old-path="fullOldPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<script>
|
||||
import DownloadViewer from '../../content_viewer/viewers/download_viewer.vue';
|
||||
import { diffModes } from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
DownloadViewer,
|
||||
},
|
||||
props: {
|
||||
diffMode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
newPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
diffModes,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="diff-file-container">
|
||||
<div class="diff-viewer">
|
||||
<div
|
||||
v-if="diffMode === $options.diffModes.replaced"
|
||||
class="two-up view row">
|
||||
<div class="col-sm-6 deleted">
|
||||
<download-viewer
|
||||
:path="oldPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-sm-6 added">
|
||||
<download-viewer
|
||||
:path="newPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="diffMode === $options.diffModes.new"
|
||||
class="added">
|
||||
<download-viewer
|
||||
:path="newPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="deleted">
|
||||
<download-viewer
|
||||
:path="oldPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<script>
|
||||
import { pixeliseValue } from '../../../lib/utils/dom_utils';
|
||||
import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ImageViewer,
|
||||
},
|
||||
props: {
|
||||
newPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
onionMaxWidth: undefined,
|
||||
onionMaxHeight: undefined,
|
||||
onionOldImgInfo: null,
|
||||
onionNewImgInfo: null,
|
||||
onionDraggerPos: 0,
|
||||
onionOpacity: 1,
|
||||
dragging: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
onionMaxPixelWidth() {
|
||||
return pixeliseValue(this.onionMaxWidth);
|
||||
},
|
||||
onionMaxPixelHeight() {
|
||||
return pixeliseValue(this.onionMaxHeight);
|
||||
},
|
||||
onionDraggerPixelPos() {
|
||||
return pixeliseValue(this.onionDraggerPos);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.body.removeEventListener('mouseup', this.stopDrag);
|
||||
this.$refs.dragger.removeEventListener('mousedown', this.startDrag);
|
||||
},
|
||||
methods: {
|
||||
dragMove(e) {
|
||||
if (!this.dragging) return;
|
||||
const left = e.pageX - this.$refs.dragTrack.getBoundingClientRect().left;
|
||||
const dragTrackWidth =
|
||||
this.$refs.dragTrack.clientWidth - this.$refs.dragger.clientWidth || 100;
|
||||
|
||||
let leftValue = left;
|
||||
if (leftValue < 0) leftValue = 0;
|
||||
if (leftValue > dragTrackWidth) leftValue = dragTrackWidth;
|
||||
|
||||
this.onionOpacity = left / dragTrackWidth;
|
||||
this.onionDraggerPos = leftValue;
|
||||
},
|
||||
startDrag() {
|
||||
this.dragging = true;
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.addEventListener('mousemove', this.dragMove);
|
||||
},
|
||||
stopDrag() {
|
||||
this.dragging = false;
|
||||
document.body.style.userSelect = '';
|
||||
document.body.removeEventListener('mousemove', this.dragMove);
|
||||
},
|
||||
prepareOnionSkin() {
|
||||
if (this.onionOldImgInfo && this.onionNewImgInfo) {
|
||||
this.onionMaxWidth = Math.max(
|
||||
this.onionOldImgInfo.renderedWidth,
|
||||
this.onionNewImgInfo.renderedWidth,
|
||||
);
|
||||
this.onionMaxHeight = Math.max(
|
||||
this.onionOldImgInfo.renderedHeight,
|
||||
this.onionNewImgInfo.renderedHeight,
|
||||
);
|
||||
|
||||
this.onionOpacity = 1;
|
||||
this.onionDraggerPos =
|
||||
this.$refs.dragTrack.clientWidth - this.$refs.dragger.clientWidth || 100;
|
||||
|
||||
document.body.addEventListener('mouseup', this.stopDrag);
|
||||
}
|
||||
},
|
||||
onionNewImgLoaded(imgInfo) {
|
||||
this.onionNewImgInfo = imgInfo;
|
||||
this.prepareOnionSkin();
|
||||
},
|
||||
onionOldImgLoaded(imgInfo) {
|
||||
this.onionOldImgInfo = imgInfo;
|
||||
this.prepareOnionSkin();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="onion-skin view">
|
||||
<div
|
||||
class="onion-skin-frame"
|
||||
:style="{
|
||||
'width': onionMaxPixelWidth,
|
||||
'height': onionMaxPixelHeight,
|
||||
'user-select': dragging === true ? 'none' : '',
|
||||
}">
|
||||
<div
|
||||
class="frame deleted"
|
||||
:style="{
|
||||
'width': onionMaxPixelWidth,
|
||||
'height': onionMaxPixelHeight,
|
||||
}">
|
||||
<image-viewer
|
||||
key="onionOldImg"
|
||||
:render-info="false"
|
||||
:path="oldPath"
|
||||
:project-path="projectPath"
|
||||
@imgLoaded="onionOldImgLoaded"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="added frame"
|
||||
ref="addedFrame"
|
||||
:style="{
|
||||
'opacity': onionOpacity,
|
||||
'width': onionMaxPixelWidth,
|
||||
'height': onionMaxPixelHeight,
|
||||
}">
|
||||
<image-viewer
|
||||
key="onionNewImg"
|
||||
:render-info="false"
|
||||
:path="newPath"
|
||||
:project-path="projectPath"
|
||||
@imgLoaded="onionNewImgLoaded"
|
||||
/>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<div class="transparent"></div>
|
||||
<div
|
||||
class="drag-track"
|
||||
ref="dragTrack"
|
||||
@mousedown="startDrag"
|
||||
@mouseup="stopDrag">
|
||||
<div
|
||||
class="dragger"
|
||||
ref="dragger"
|
||||
:style="{ 'left': onionDraggerPixelPos }">
|
||||
</div>
|
||||
</div>
|
||||
<div class="opaque"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
<script>
|
||||
import _ from 'underscore';
|
||||
import { pixeliseValue } from '../../../lib/utils/dom_utils';
|
||||
import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ImageViewer,
|
||||
},
|
||||
props: {
|
||||
newPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragging: false,
|
||||
swipeOldImgInfo: null,
|
||||
swipeNewImgInfo: null,
|
||||
swipeMaxWidth: undefined,
|
||||
swipeMaxHeight: undefined,
|
||||
swipeBarPos: 1,
|
||||
swipeWrapWidth: undefined,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
swipeMaxPixelWidth() {
|
||||
return pixeliseValue(this.swipeMaxWidth);
|
||||
},
|
||||
swipeMaxPixelHeight() {
|
||||
return pixeliseValue(this.swipeMaxHeight);
|
||||
},
|
||||
swipeWrapPixelWidth() {
|
||||
return pixeliseValue(this.swipeWrapWidth);
|
||||
},
|
||||
swipeBarPixelPos() {
|
||||
return pixeliseValue(this.swipeBarPos);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.resizeThrottled, false);
|
||||
document.body.removeEventListener('mouseup', this.stopDrag);
|
||||
document.body.removeEventListener('mousemove', this.dragMove);
|
||||
},
|
||||
mounted() {
|
||||
window.addEventListener('resize', this.resize, false);
|
||||
},
|
||||
methods: {
|
||||
dragMove(e) {
|
||||
if (!this.dragging) return;
|
||||
|
||||
let leftValue = e.pageX - this.$refs.swipeFrame.getBoundingClientRect().left;
|
||||
const spaceLeft = 20;
|
||||
const { clientWidth } = this.$refs.swipeFrame;
|
||||
if (leftValue <= 0) {
|
||||
leftValue = 0;
|
||||
} else if (leftValue > clientWidth - spaceLeft) {
|
||||
leftValue = clientWidth - spaceLeft;
|
||||
}
|
||||
|
||||
this.swipeWrapWidth = this.swipeMaxWidth - leftValue;
|
||||
this.swipeBarPos = leftValue;
|
||||
},
|
||||
startDrag() {
|
||||
this.dragging = true;
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.addEventListener('mousemove', this.dragMove);
|
||||
},
|
||||
stopDrag() {
|
||||
this.dragging = false;
|
||||
document.body.style.userSelect = '';
|
||||
document.body.removeEventListener('mousemove', this.dragMove);
|
||||
},
|
||||
prepareSwipe() {
|
||||
if (this.swipeOldImgInfo && this.swipeNewImgInfo) {
|
||||
// Add 2 for border width
|
||||
this.swipeMaxWidth =
|
||||
Math.max(this.swipeOldImgInfo.renderedWidth, this.swipeNewImgInfo.renderedWidth) + 2;
|
||||
this.swipeWrapWidth = this.swipeMaxWidth;
|
||||
this.swipeMaxHeight =
|
||||
Math.max(this.swipeOldImgInfo.renderedHeight, this.swipeNewImgInfo.renderedHeight) + 2;
|
||||
|
||||
document.body.addEventListener('mouseup', this.stopDrag);
|
||||
}
|
||||
},
|
||||
swipeNewImgLoaded(imgInfo) {
|
||||
this.swipeNewImgInfo = imgInfo;
|
||||
this.prepareSwipe();
|
||||
},
|
||||
swipeOldImgLoaded(imgInfo) {
|
||||
this.swipeOldImgInfo = imgInfo;
|
||||
this.prepareSwipe();
|
||||
},
|
||||
resize: _.throttle(function throttledResize() {
|
||||
this.swipeBarPos = 0;
|
||||
}, 400),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="swipe view">
|
||||
<div
|
||||
class="swipe-frame"
|
||||
ref="swipeFrame"
|
||||
:style="{
|
||||
'width': swipeMaxPixelWidth,
|
||||
'height': swipeMaxPixelHeight,
|
||||
}">
|
||||
<div class="frame deleted">
|
||||
<image-viewer
|
||||
key="swipeOldImg"
|
||||
ref="swipeOldImg"
|
||||
:render-info="false"
|
||||
:path="oldPath"
|
||||
:project-path="projectPath"
|
||||
@imgLoaded="swipeOldImgLoaded"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="swipe-wrap"
|
||||
ref="swipeWrap"
|
||||
:style="{
|
||||
'width': swipeWrapPixelWidth,
|
||||
'height': swipeMaxPixelHeight,
|
||||
}">
|
||||
<div class="frame added">
|
||||
<image-viewer
|
||||
key="swipeNewImg"
|
||||
:render-info="false"
|
||||
:path="newPath"
|
||||
:project-path="projectPath"
|
||||
@imgLoaded="swipeNewImgLoaded"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
class="swipe-bar"
|
||||
ref="swipeBar"
|
||||
@mousedown="startDrag"
|
||||
@mouseup="stopDrag"
|
||||
:style="{ 'left': swipeBarPixelPos }">
|
||||
<span class="top-handle"></span>
|
||||
<span class="bottom-handle"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<script>
|
||||
import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ImageViewer,
|
||||
},
|
||||
props: {
|
||||
newPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="two-up view row">
|
||||
<div class="col-sm-6 frame deleted">
|
||||
<image-viewer
|
||||
:path="oldPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-sm-6 frame added">
|
||||
<image-viewer
|
||||
:path="newPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
<script>
|
||||
import ImageViewer from '../../content_viewer/viewers/image_viewer.vue';
|
||||
import TwoUpViewer from './image_diff/two_up_viewer.vue';
|
||||
import SwipeViewer from './image_diff/swipe_viewer.vue';
|
||||
import OnionSkinViewer from './image_diff/onion_skin_viewer.vue';
|
||||
import { diffModes, imageViewMode } from '../constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ImageViewer,
|
||||
TwoUpViewer,
|
||||
SwipeViewer,
|
||||
OnionSkinViewer,
|
||||
},
|
||||
props: {
|
||||
diffMode: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
newPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
oldPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
mode: imageViewMode.twoup,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeMode(newMode) {
|
||||
this.mode = newMode;
|
||||
},
|
||||
},
|
||||
diffModes,
|
||||
imageViewMode,
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="diff-file-container">
|
||||
<div
|
||||
class="diff-viewer"
|
||||
v-if="diffMode === $options.diffModes.replaced">
|
||||
<div class="image js-replaced-image">
|
||||
<two-up-viewer
|
||||
v-if="mode === $options.imageViewMode.twoup"
|
||||
v-bind="$props"/>
|
||||
<swipe-viewer
|
||||
v-else-if="mode === $options.imageViewMode.swipe"
|
||||
v-bind="$props"/>
|
||||
<onion-skin-viewer
|
||||
v-else-if="mode === $options.imageViewMode.onion"
|
||||
v-bind="$props"/>
|
||||
</div>
|
||||
<div class="view-modes">
|
||||
<ul class="view-modes-menu">
|
||||
<li
|
||||
:class="{
|
||||
active: mode === $options.imageViewMode.twoup
|
||||
}"
|
||||
@click="changeMode($options.imageViewMode.twoup)">
|
||||
{{ s__('ImageDiffViewer|2-up') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
active: mode === $options.imageViewMode.swipe
|
||||
}"
|
||||
@click="changeMode($options.imageViewMode.swipe)">
|
||||
{{ s__('ImageDiffViewer|Swipe') }}
|
||||
</li>
|
||||
<li
|
||||
:class="{
|
||||
active: mode === $options.imageViewMode.onion
|
||||
}"
|
||||
@click="changeMode($options.imageViewMode.onion)">
|
||||
{{ s__('ImageDiffViewer|Onion skin') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="note-container"></div>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="diffMode === $options.diffModes.new"
|
||||
class="diff-viewer added">
|
||||
<image-viewer
|
||||
:path="newPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="diff-viewer deleted">
|
||||
<image-viewer
|
||||
:path="oldPath"
|
||||
:project-path="projectPath"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export function pixeliseValue(val) {
|
||||
return val ? `${val}px` : '';
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
|
@ -400,3 +400,51 @@ span.idiff {
|
|||
color: $common-gray-light;
|
||||
border: 1px solid $common-gray-light;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.file-container {
|
||||
background-color: $gray-darker;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
text-align: center;
|
||||
|
||||
.file-content {
|
||||
padding: $gl-padding;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
img {
|
||||
max-width: 90%;
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.is-zoomable {
|
||||
cursor: pointer;
|
||||
cursor: zoom-in;
|
||||
|
||||
&.is-zoomed {
|
||||
cursor: pointer;
|
||||
cursor: zoom-out;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
margin-right: $gl-padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-info {
|
||||
font-size: $label-font-size;
|
||||
color: $diff-image-info-color;
|
||||
}
|
||||
}
|
||||
|
||||
.md-previewer {
|
||||
padding: $gl-padding;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,8 +189,22 @@
|
|||
|
||||
img {
|
||||
border: 1px solid $white-light;
|
||||
background-image: linear-gradient(45deg, $border-color 25%, transparent 25%, transparent 75%, $border-color 75%, $border-color 100%),
|
||||
linear-gradient(45deg, $border-color 25%, transparent 25%, transparent 75%, $border-color 75%, $border-color 100%);
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
$border-color 25%,
|
||||
transparent 25%,
|
||||
transparent 75%,
|
||||
$border-color 75%,
|
||||
$border-color 100%
|
||||
),
|
||||
linear-gradient(
|
||||
45deg,
|
||||
$border-color 25%,
|
||||
transparent 25%,
|
||||
transparent 75%,
|
||||
$border-color 75%,
|
||||
$border-color 100%
|
||||
);
|
||||
background-size: 10px 10px;
|
||||
background-position: 0 0, 5px 5px;
|
||||
max-width: 100%;
|
||||
|
|
@ -395,6 +409,69 @@
|
|||
.line_content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.diff-file-container {
|
||||
.frame.deleted {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
|
||||
.image_file img {
|
||||
border: 1px solid $deleted;
|
||||
}
|
||||
}
|
||||
|
||||
.frame.added {
|
||||
border: 0;
|
||||
background-color: inherit;
|
||||
|
||||
.image_file img {
|
||||
border: 1px solid $added;
|
||||
}
|
||||
}
|
||||
|
||||
.swipe.view,
|
||||
.onion-skin.view {
|
||||
.swipe-wrap {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.frame.deleted {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.swipe-bar {
|
||||
top: 0;
|
||||
|
||||
.top-handle {
|
||||
top: -14px;
|
||||
left: -7px;
|
||||
}
|
||||
|
||||
.bottom-handle {
|
||||
bottom: -14px;
|
||||
left: -7px;
|
||||
}
|
||||
}
|
||||
|
||||
.file-container {
|
||||
display: inline-block;
|
||||
|
||||
.file-content {
|
||||
padding: 0;
|
||||
|
||||
img {
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.onion-skin.view .controls {
|
||||
bottom: -25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.file-content .diff-file {
|
||||
|
|
@ -536,7 +613,7 @@
|
|||
margin-right: 0;
|
||||
border-color: $white-light;
|
||||
cursor: pointer;
|
||||
transition: all .1s ease-out;
|
||||
transition: all 0.1s ease-out;
|
||||
|
||||
@for $i from 1 through 4 {
|
||||
&:nth-child(#{$i}) {
|
||||
|
|
@ -563,7 +640,7 @@
|
|||
height: 24px;
|
||||
border-radius: 50%;
|
||||
padding: 0;
|
||||
transition: transform .1s ease-out;
|
||||
transition: transform 0.1s ease-out;
|
||||
z-index: 100;
|
||||
|
||||
.collapse-icon {
|
||||
|
|
@ -708,11 +785,35 @@
|
|||
width: 100%;
|
||||
height: 10px;
|
||||
background-color: $white-light;
|
||||
background-image: linear-gradient(45deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%),
|
||||
linear-gradient(225deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%),
|
||||
linear-gradient(135deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%),
|
||||
linear-gradient(-45deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%);
|
||||
background-position: 5px 5px,0 5px,0 5px,5px 5px;
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
transparent,
|
||||
transparent 73%,
|
||||
$diff-jagged-border-gradient-color 75%,
|
||||
$white-light 80%
|
||||
),
|
||||
linear-gradient(
|
||||
225deg,
|
||||
transparent,
|
||||
transparent 73%,
|
||||
$diff-jagged-border-gradient-color 75%,
|
||||
$white-light 80%
|
||||
),
|
||||
linear-gradient(
|
||||
135deg,
|
||||
transparent,
|
||||
transparent 73%,
|
||||
$diff-jagged-border-gradient-color 75%,
|
||||
$white-light 80%
|
||||
),
|
||||
linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 73%,
|
||||
$diff-jagged-border-gradient-color 75%,
|
||||
$white-light 80%
|
||||
);
|
||||
background-position: 5px 5px, 0 5px, 0 5px, 5px 5px;
|
||||
background-size: 10px 10px;
|
||||
background-repeat: repeat;
|
||||
}
|
||||
|
|
@ -750,11 +851,16 @@
|
|||
.frame.click-to-comment {
|
||||
position: relative;
|
||||
cursor: image-url('illustrations/image_comment_light_cursor.svg')
|
||||
$image-comment-cursor-left-offset $image-comment-cursor-top-offset, auto;
|
||||
$image-comment-cursor-left-offset $image-comment-cursor-top-offset,
|
||||
auto;
|
||||
|
||||
// Retina cursor
|
||||
cursor: -webkit-image-set(image-url('illustrations/image_comment_light_cursor.svg') 1x, image-url('illustrations/image_comment_light_cursor@2x.svg') 2x)
|
||||
$image-comment-cursor-left-offset $image-comment-cursor-top-offset, auto;
|
||||
cursor: -webkit-image-set(
|
||||
image-url('illustrations/image_comment_light_cursor.svg') 1x,
|
||||
image-url('illustrations/image_comment_light_cursor@2x.svg') 2x
|
||||
)
|
||||
$image-comment-cursor-left-offset $image-comment-cursor-top-offset,
|
||||
auto;
|
||||
|
||||
.comment-indicator {
|
||||
position: absolute;
|
||||
|
|
@ -840,7 +946,7 @@
|
|||
|
||||
.diff-notes-collapse,
|
||||
.note,
|
||||
.discussion-reply-holder, {
|
||||
.discussion-reply-holder {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -335,7 +335,6 @@
|
|||
|
||||
img {
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
.isZoomable {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Web IDE supports now Image + Download Diff Viewing
|
||||
merge_request: 18768
|
||||
author:
|
||||
type: added
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -152,6 +152,53 @@ describe('IDE store file mutations', () => {
|
|||
|
||||
expect(localFile.mrChange.diff).toBe('ABC');
|
||||
});
|
||||
|
||||
it('has diffMode replaced by default', () => {
|
||||
mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
|
||||
file: localFile,
|
||||
mrChange: {
|
||||
diff: 'ABC',
|
||||
},
|
||||
});
|
||||
|
||||
expect(localFile.mrChange.diffMode).toBe('replaced');
|
||||
});
|
||||
|
||||
it('has diffMode new', () => {
|
||||
mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
|
||||
file: localFile,
|
||||
mrChange: {
|
||||
diff: 'ABC',
|
||||
new_file: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(localFile.mrChange.diffMode).toBe('new');
|
||||
});
|
||||
|
||||
it('has diffMode deleted', () => {
|
||||
mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
|
||||
file: localFile,
|
||||
mrChange: {
|
||||
diff: 'ABC',
|
||||
deleted_file: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(localFile.mrChange.diffMode).toBe('deleted');
|
||||
});
|
||||
|
||||
it('has diffMode renamed', () => {
|
||||
mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
|
||||
file: localFile,
|
||||
mrChange: {
|
||||
diff: 'ABC',
|
||||
renamed_file: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(localFile.mrChange.diffMode).toBe('renamed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('DISCARD_FILE_CHANGES', () => {
|
||||
|
|
|
|||
|
|
@ -2,3 +2,6 @@ export const FIXTURES_PATH = '/base/spec/javascripts/fixtures';
|
|||
export const TEST_HOST = 'http://test.host';
|
||||
|
||||
export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/one_white_pixel.png`;
|
||||
|
||||
export const GREEN_BOX_IMAGE_URL = `${FIXTURES_PATH}/images/green_box.png`;
|
||||
export const RED_BOX_IMAGE_URL = `${FIXTURES_PATH}/images/red_box.png`;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
|
||||
|
||||
describe('ContentViewer', () => {
|
||||
let vm;
|
||||
|
|
@ -41,12 +42,12 @@ describe('ContentViewer', () => {
|
|||
|
||||
it('renders image preview', done => {
|
||||
createComponent({
|
||||
path: 'test.jpg',
|
||||
path: GREEN_BOX_IMAGE_URL,
|
||||
fileSize: 1024,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe('test.jpg');
|
||||
expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
|
||||
|
||||
done();
|
||||
});
|
||||
|
|
@ -59,9 +60,8 @@ describe('ContentViewer', () => {
|
|||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain(
|
||||
'test.abc (1.00 KiB)',
|
||||
);
|
||||
expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain('test.abc');
|
||||
expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain('(1.00 KiB)');
|
||||
expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toContain('Download');
|
||||
|
||||
done();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import Vue from 'vue';
|
||||
import diffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
|
||||
|
||||
describe('DiffViewer', () => {
|
||||
let vm;
|
||||
|
||||
function createComponent(props) {
|
||||
const DiffViewer = Vue.extend(diffViewer);
|
||||
vm = mountComponent(DiffViewer, props);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders image diff', done => {
|
||||
window.gon = {
|
||||
relative_url_root: '',
|
||||
};
|
||||
|
||||
createComponent({
|
||||
diffMode: 'replaced',
|
||||
newPath: GREEN_BOX_IMAGE_URL,
|
||||
newSha: 'ABC',
|
||||
oldPath: RED_BOX_IMAGE_URL,
|
||||
oldSha: 'DEF',
|
||||
projectPath: '',
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
|
||||
`//raw/DEF/${RED_BOX_IMAGE_URL}`,
|
||||
);
|
||||
|
||||
expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
|
||||
`//raw/ABC/${GREEN_BOX_IMAGE_URL}`,
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders fallback download diff display', done => {
|
||||
createComponent({
|
||||
diffMode: 'replaced',
|
||||
newPath: 'test.abc',
|
||||
newSha: 'ABC',
|
||||
oldPath: 'testold.abc',
|
||||
oldSha: 'DEF',
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain(
|
||||
'testold.abc',
|
||||
);
|
||||
expect(vm.$el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain(
|
||||
'Download',
|
||||
);
|
||||
|
||||
expect(vm.$el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc');
|
||||
expect(vm.$el.querySelector('.added .btn.btn-default').textContent.trim()).toContain(
|
||||
'Download',
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
import Vue from 'vue';
|
||||
import imageDiffViewer from '~/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
|
||||
|
||||
describe('ImageDiffViewer', () => {
|
||||
let vm;
|
||||
|
||||
function createComponent(props) {
|
||||
const ImageDiffViewer = Vue.extend(imageDiffViewer);
|
||||
vm = mountComponent(ImageDiffViewer, props);
|
||||
}
|
||||
|
||||
const triggerEvent = (eventName, el = vm.$el, clientX = 0) => {
|
||||
const event = document.createEvent('MouseEvents');
|
||||
event.initMouseEvent(
|
||||
eventName,
|
||||
true,
|
||||
true,
|
||||
window,
|
||||
1,
|
||||
clientX,
|
||||
0,
|
||||
clientX,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
0,
|
||||
null,
|
||||
);
|
||||
|
||||
el.dispatchEvent(event);
|
||||
};
|
||||
|
||||
const dragSlider = (sliderElement, dragPixel = 20) => {
|
||||
triggerEvent('mousedown', sliderElement);
|
||||
triggerEvent('mousemove', document.body, dragPixel);
|
||||
triggerEvent('mouseup', document.body);
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders image diff for replaced', done => {
|
||||
createComponent({
|
||||
diffMode: 'replaced',
|
||||
newPath: GREEN_BOX_IMAGE_URL,
|
||||
oldPath: RED_BOX_IMAGE_URL,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
|
||||
GREEN_BOX_IMAGE_URL,
|
||||
);
|
||||
expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
|
||||
RED_BOX_IMAGE_URL,
|
||||
);
|
||||
|
||||
expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('2-up');
|
||||
expect(vm.$el.querySelector('.view-modes-menu li:nth-child(2)').textContent.trim()).toBe(
|
||||
'Swipe',
|
||||
);
|
||||
expect(vm.$el.querySelector('.view-modes-menu li:nth-child(3)').textContent.trim()).toBe(
|
||||
'Onion skin',
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders image diff for new', done => {
|
||||
createComponent({
|
||||
diffMode: 'new',
|
||||
newPath: GREEN_BOX_IMAGE_URL,
|
||||
oldPath: '',
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
|
||||
GREEN_BOX_IMAGE_URL,
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders image diff for deleted', done => {
|
||||
createComponent({
|
||||
diffMode: 'deleted',
|
||||
newPath: '',
|
||||
oldPath: RED_BOX_IMAGE_URL,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
|
||||
RED_BOX_IMAGE_URL,
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('swipeMode', () => {
|
||||
beforeEach(done => {
|
||||
createComponent({
|
||||
diffMode: 'replaced',
|
||||
newPath: GREEN_BOX_IMAGE_URL,
|
||||
oldPath: RED_BOX_IMAGE_URL,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('switches to Swipe Mode', done => {
|
||||
vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('Swipe');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('drag handler is working', done => {
|
||||
vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('1px');
|
||||
expect(vm.$el.querySelector('.top-handle')).not.toBeNull();
|
||||
|
||||
dragSlider(vm.$el.querySelector('.swipe-bar'), 40);
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('-20px');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onionSkin', () => {
|
||||
beforeEach(done => {
|
||||
createComponent({
|
||||
diffMode: 'replaced',
|
||||
newPath: GREEN_BOX_IMAGE_URL,
|
||||
oldPath: RED_BOX_IMAGE_URL,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('switches to Onion Skin Mode', done => {
|
||||
vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe(
|
||||
'Onion skin',
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('has working drag handler', done => {
|
||||
vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.dragger').style.left).toBe('100px');
|
||||
|
||||
dragSlider(vm.$el.querySelector('.dragger'));
|
||||
|
||||
vm.$nextTick(() => {
|
||||
expect(vm.$el.querySelector('.dragger').style.left).toBe('20px');
|
||||
expect(vm.$el.querySelector('.added.frame').style.opacity).toBe('0.2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import * as domUtils from '~/vue_shared/components/lib/utils/dom_utils';
|
||||
|
||||
describe('domUtils', () => {
|
||||
describe('pixeliseValue', () => {
|
||||
it('should add px to a given Number', () => {
|
||||
expect(domUtils.pixeliseValue(12)).toEqual('12px');
|
||||
});
|
||||
|
||||
it('should not add px to 0', () => {
|
||||
expect(domUtils.pixeliseValue(0)).toEqual('');
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue