summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bennett <lbennett@gitlab.com>2019-05-06 20:18:48 +0100
committersarahghp <sarah.groff.palermo@gmail.com>2019-05-29 11:24:06 -0400
commitd36869d14781b1c97bb069bde1f00ec23c5ac9db (patch)
treeeb3e41ea945473b4c0c02f68b9ea781e377ff8b3
parentee277ce0b69680531f57dd3572b14877aaa79045 (diff)
downloadgitlab-ce-d36869d14781b1c97bb069bde1f00ec23c5ac9db.tar.gz
Build visual_review_toolbar with webpack
It takes a lot of lines to move a script
-rw-r--r--app/assets/javascripts/visual_review_toolbar/index.js792
-rw-r--r--config/karma.config.js2
-rw-r--r--config/webpack.config.js60
-rw-r--r--public/visual-review-toolbar.js198
4 files changed, 1026 insertions, 26 deletions
diff --git a/app/assets/javascripts/visual_review_toolbar/index.js b/app/assets/javascripts/visual_review_toolbar/index.js
new file mode 100644
index 00000000000..0a9e497c3cf
--- /dev/null
+++ b/app/assets/javascripts/visual_review_toolbar/index.js
@@ -0,0 +1,792 @@
+///////////////////////////////////////////////
+/////////////////// STYLES ////////////////////
+///////////////////////////////////////////////
+
+const buttonClearStyles = `
+ -webkit-appearance: none;
+`;
+
+const buttonBaseStyles = `
+ cursor: pointer;
+ transition: background-color 100ms linear, border-color 100ms linear, color 100ms linear, box-shadow 100ms linear;
+`;
+
+const buttonSuccessActiveStyles = `
+ background-color: #168f48;
+ border-color: #12753a;
+ color: #fff;
+`;
+
+const buttonSuccessHoverStyles = `
+ color: #fff;
+ background-color: #137e3f;
+ border-color: #127339;
+`;
+
+const buttonSuccessStyles = `
+ ${buttonBaseStyles}
+ background-color: #1aaa55;
+ border: 1px solid #168f48;
+ color: #fff;
+`;
+
+const buttonSecondaryStyles = `
+ ${buttonBaseStyles}
+ background: none #fff;
+ margin: 0 .5rem;
+ border: 1px solid #e3e3e3;
+`;
+
+const buttonSecondaryActiveStyles = `
+ color: #2e2e2e;
+ background-color: #e1e1e1;
+ border-color: #dadada;
+`;
+
+const buttonSecondaryHoverStyles = `
+ background-color: #f0f0f0;
+ border-color: #e3e3e3;
+ color: #2e2e2e;
+`;
+
+const buttonWideStyles = `
+ width: 100%;
+`;
+
+const buttonWrapperStyles = `
+ margin-top: 1rem;
+ display: flex;
+ align-items: baseline;
+ justify-content: flex-end;
+`;
+
+const collapseStyles = `
+ ${buttonBaseStyles}
+ width: 2.4rem;
+ height: 2.2rem;
+ margin-left: 1rem;
+ padding: .5rem;
+`;
+
+const collapseClosedStyles = `
+ ${collapseStyles}
+ align-self: center;
+`;
+
+const collapseOpenStyles = `
+ ${collapseStyles}
+`;
+
+const checkboxLabelStyles = `
+ padding: 0 .2rem;
+`;
+
+const checkboxWrapperStyles = `
+ display: flex;
+ align-items: baseline;
+`;
+
+const formStyles = `
+ display: flex;
+ flex-direction: column;
+ width: 100%
+`;
+
+const labelStyles = `
+ font-weight: 600;
+ display: inline-block;
+ width: 100%;
+`;
+
+const linkStyles = `
+ color: #1b69b6;
+ text-decoration: none;
+ background-color: transparent;
+ background-image: none;
+`;
+
+const messageStyles = `
+ padding: .25rem 0;
+ margin: 0;
+ line-height: 1.2rem;
+`;
+
+const metadataNoteStyles = `
+ font-size: .7rem;
+ line-height: 1rem;
+ color: #666;
+ margin-bottom: 0;
+`;
+
+const inputStyles = `
+ width: 100%;
+ border: 1px solid #dfdfdf;
+ border-radius: 4px;
+ padding: .1rem .2rem;
+ min-height: 2rem;
+ max-width: 17rem;
+`;
+
+const svgInnerStyles = `
+ pointer-events: none;
+`;
+
+const wrapperClosedStyles = `
+ max-width: 3.4rem;
+ max-height: 3.4rem;
+`;
+
+const wrapperOpenStyles = `
+ max-width: 22rem;
+ max-height: 22rem;
+`;
+
+const wrapperStyles = `
+ max-width: 22rem;
+ max-height: 22rem;
+ overflow: scroll;
+ position: fixed;
+ bottom: 1rem;
+ right: 1rem;
+ display: flex;
+ flex-direction: row-reverse;
+ padding: 1rem;
+ background-color: #fff;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell,
+ 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+ 'Noto Color Emoji';
+ font-size: .8rem;
+ font-weight: 400;
+ color: #2e2e2e;
+`;
+
+const gitlabStyles = `
+ #gitlab-collapse > * {
+ ${svgInnerStyles}
+ }
+
+ #gitlab-form-wrapper {
+ ${formStyles}
+ }
+
+ #gitlab-review-container {
+ ${wrapperStyles}
+ }
+
+ .gitlab-open-wrapper {
+ ${wrapperOpenStyles}
+ }
+
+ .gitlab-closed-wrapper {
+ ${wrapperClosedStyles}
+ }
+
+ .gitlab-button-secondary {
+ ${buttonSecondaryStyles}
+ }
+
+ .gitlab-button-secondary:hover {
+ ${buttonSecondaryHoverStyles}
+ }
+
+ .gitlab-button-secondary:active {
+ ${buttonSecondaryActiveStyles}
+ }
+
+ .gitlab-button-success:hover {
+ ${buttonSuccessHoverStyles}
+ }
+
+ .gitlab-button-success:active {
+ ${buttonSuccessActiveStyles}
+ }
+
+ .gitlab-button-success {
+ ${buttonSuccessStyles}
+ }
+
+ .gitlab-button-wide {
+ ${buttonWideStyles}
+ }
+
+ .gitlab-button-wrapper {
+ ${buttonWrapperStyles}
+ }
+
+ .gitlab-collapse-closed {
+ ${collapseClosedStyles}
+ }
+
+ .gitlab-collapse-open {
+ ${collapseOpenStyles}
+ }
+
+ .gitlab-checkbox-label {
+ ${checkboxLabelStyles}
+ }
+
+ .gitlab-checkbox-wrapper {
+ ${checkboxWrapperStyles}
+ }
+
+ .gitlab-label {
+ ${labelStyles}
+ }
+
+ .gitlab-link {
+ ${linkStyles}
+ }
+
+ .gitlab-message {
+ ${messageStyles}
+ }
+
+ .gitlab-metadata-note {
+ ${metadataNoteStyles}
+ }
+
+ .gitlab-input {
+ ${inputStyles}
+ }
+`;
+
+function addStylesheet() {
+ const styleEl = document.createElement('style');
+ styleEl.insertAdjacentHTML('beforeend', gitlabStyles);
+ document.head.appendChild(styleEl);
+}
+
+///////////////////////////////////////////////
+/////////////////// STATE ////////////////////
+///////////////////////////////////////////////
+const data = {};
+
+///////////////////////////////////////////////
+///////////////// COMPONENTS //////////////////
+///////////////////////////////////////////////
+const note = `
+ <p id='gitlab-validation-note' class='gitlab-message'></p>
+`;
+
+const comment = `
+ <div>
+ <textarea id='gitlab-comment' name='gitlab-comment' rows='3' placeholder='Enter your feedback or idea' class='gitlab-input'></textarea>
+ ${note}
+ <p class='gitlab-metadata-note'>Additional metadata will be included: browser, OS, current page, user agent, and viewport dimensions.</p>
+ </div>
+ <div class='gitlab-button-wrapper''>
+ <button class='gitlab-button-secondary' style='${buttonClearStyles}' type='button' id='gitlab-logout-button'> Logout </button>
+ <button class='gitlab-button-success' style='${buttonClearStyles}' type='button' id='gitlab-comment-button'> Send feedback </button>
+ </div>
+`;
+
+const commentIcon = `
+<<<<<<< HEAD:public/visual-review-toolbar.js
+ <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="gitlab-comment-icon"/></svg>
+`
+=======
+ <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="a"/></svg>
+`;
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+
+const compressIcon = `
+ <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/compress</title><path d="M5.27 12.182l-1.562 1.561a1 1 0 0 1-1.414 0h-.001a1 1 0 0 1 0-1.415l1.56-1.56L2.44 9.353a.5.5 0 0 1 .353-.854H7.09a.5.5 0 0 1 .5.5v4.294a.5.5 0 0 1-.853.353l-1.467-1.465zm6.911-6.914l1.464 1.464a.5.5 0 0 1-.353.854H8.999a.5.5 0 0 1-.5-.5V2.793a.5.5 0 0 1 .854-.354l1.414 1.415 1.56-1.561a1 1 0 1 1 1.415 1.414l-1.561 1.56z" id="gitlab-compress-icon"/></svg>
+`;
+
+const collapseButton = `
+ <button id='gitlab-collapse' style='${buttonClearStyles}' class='gitlab-collapse-open gitlab-button-secondary'>${compressIcon}</button>
+`;
+
+const form = content => `
+ <div id='gitlab-form-wrapper'>
+ ${content}
+ </div>
+`;
+
+const login = `
+ <div>
+ <label for='gitlab-token' class='gitlab-label'>Enter your <a class='gitlab-link' href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">personal access token</a></label>
+ <input class='gitlab-input' type='password' id='gitlab-token' name='gitlab-token'>
+ ${note}
+ </div>
+ <div class='gitlab-checkbox-wrapper'>
+ <input type="checkbox" id="remember_token" name="remember_token" value="remember">
+ <label for="remember_token" class='gitlab-checkbox-label'>Remember me</label>
+ </div>
+ <div class='gitlab-button-wrapper'>
+ <button class='gitlab-button-wide gitlab-button-success' style='${buttonClearStyles}' type='button' id='gitlab-login'> Submit </button>
+ </div>
+`;
+
+///////////////////////////////////////////////
+//////////////// INTERACTIONS /////////////////
+///////////////////////////////////////////////
+
+// from https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator
+function getBrowserId(sUsrAg) {
+ var aKeys = ['MSIE', 'Edge', 'Firefox', 'Safari', 'Chrome', 'Opera'],
+ nIdx = aKeys.length - 1;
+
+ for (nIdx; nIdx > -1 && sUsrAg.indexOf(aKeys[nIdx]) === -1; nIdx--);
+ return aKeys[nIdx];
+}
+
+<<<<<<< HEAD:public/visual-review-toolbar.js
+function addCommentForm () {
+ const formWrapper = document.getElementById('gitlab-form-wrapper');
+ formWrapper.innerHTML = comment;
+=======
+function addCommentButtonEvent() {
+ // get user agent data
+ const {
+ innerWidth,
+ innerHeight,
+ location: { href },
+ navigator: { platform, userAgent },
+ } = window;
+ const browser = getBrowserId(userAgent);
+
+ const scriptName = 'ReviewAppToolbar';
+ const projectId = document
+ .querySelector(`script[data-name='${scriptName}']`)
+ .getAttribute('data-project');
+ const discussionId = document
+ .querySelector(`script[data-name='${scriptName}']`)
+ .getAttribute('data-discussion');
+ const mrUrl = document
+ .querySelector(`script[data-name='${scriptName}']`)
+ .getAttribute('data-mr-url');
+ const commentButton = document.getElementById('gitlab-comment-button');
+
+ const details = {
+ href,
+ platform,
+ browser,
+ userAgent,
+ innerWidth,
+ innerHeight,
+ projectId,
+ discussionId,
+ mrUrl,
+ };
+
+ commentButton.onclick = postComment.bind(null, details);
+}
+
+function addCollapseEvent() {
+ const collapseButton = document.getElementById('gitlab-collapse');
+ collapseButton.onclick = collapseForm;
+}
+
+function addCommentForm() {
+ const formWrapper = document.getElementById('gitlab-form-wrapper');
+ formWrapper.innerHTML = comment;
+ removeButtonAndClickEvent('gitlab-login', authorizeUser);
+ addCommentButtonEvent();
+ addLogoutButtonEvent();
+}
+
+function addLoginButtonEvent() {
+ const loginButton = document.getElementById('gitlab-login');
+ if (loginButton) {
+ loginButton.onclick = authorizeUser;
+ }
+}
+
+function addLogoutButtonEvent() {
+ const logoutButton = document.getElementById('gitlab-logout-button');
+ if (logoutButton) {
+ logoutButton.onclick = logoutUser;
+ }
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+}
+
+function addLoginForm() {
+ const formWrapper = document.getElementById('gitlab-form-wrapper');
+ formWrapper.innerHTML = login;
+}
+
+<<<<<<< HEAD:public/visual-review-toolbar.js
+function authorizeUser () {
+=======
+function authorizeUser() {
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+ // Clear any old errors
+ clearNote('gitlab-token');
+
+ const token = document.getElementById('gitlab-token').value;
+ const rememberMe = document.getElementById('remember_token').checked;
+
+ if (!token) {
+ postError('Please enter your token.', 'gitlab-token');
+ return;
+ }
+
+ if (rememberMe) {
+ storeToken(token);
+ }
+
+ authSuccess(token);
+ return;
+}
+
+function authSuccess(token) {
+ data.token = token;
+ addCommentForm();
+}
+
+function clearNote(inputId) {
+ const note = document.getElementById('gitlab-validation-note');
+ note.innerText = '';
+ note.style.color = '';
+
+ if (inputId) {
+ const field = document.getElementById(inputId);
+ field.style.borderColor = '';
+ }
+}
+
+<<<<<<< HEAD:public/visual-review-toolbar.js
+function confirmAndClear (mergeRequestId) {
+=======
+function confirmAndClear(discussionId) {
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+ const commentButton = document.getElementById('gitlab-comment-button');
+ const note = document.getElementById('gitlab-validation-note');
+
+ commentButton.innerText = 'Feedback sent';
+ note.innerText = `Your comment was successfully posted to merge request #${mergeRequestId}`;
+
+ setTimeout(resetCommentButton, 1000);
+}
+
+<<<<<<< HEAD:public/visual-review-toolbar.js
+function getInitialState () {
+ const { localStorage } = window;
+
+ try {
+ let token = localStorage.getItem('token');
+
+ if (token) {
+ data.token = token;
+ return comment;
+ }
+
+ return login;
+
+ } catch (err) {
+ return login;
+ }
+}
+
+function getProjectDetails () {
+ const { innerWidth,
+ innerHeight,
+ location: { href },
+ navigator: {
+ platform, userAgent
+ } } = window;
+ const browser = getBrowserId(userAgent);
+=======
+function collapseForm() {
+ const container = document.getElementById('gitlab-review-container');
+ const collapseButton = document.getElementById('gitlab-collapse');
+ const form = document.getElementById('gitlab-form-wrapper');
+
+ container.classList.replace('gitlab-open-wrapper', 'gitlab-closed-wrapper');
+ container.style.backgroundColor = 'rgba(255, 255, 255, 0)';
+ form.style.display = 'none';
+
+ collapseButton.classList.replace('gitlab-collapse-open', 'gitlab-collapse-closed');
+ collapseButton.innerHTML = commentIcon;
+ collapseButton.onclick = expandForm;
+}
+
+function expandForm() {
+ const container = document.getElementById('gitlab-review-container');
+ const collapseButton = document.getElementById('gitlab-collapse');
+ const form = document.getElementById('gitlab-form-wrapper');
+
+ container.classList.replace('gitlab-closed-wrapper', 'gitlab-open-wrapper');
+ container.style.backgroundColor = 'rgba(255, 255, 255, 1)';
+ form.style.display = 'flex';
+
+ collapseButton.classList.replace('gitlab-collapse-closed', 'gitlab-collapse-open');
+ collapseButton.innerHTML = compressIcon;
+ collapseButton.onclick = collapseForm;
+}
+
+function getInitialState() {
+ const { localStorage } = window;
+
+ if (!localStorage || !localStorage.getItem('token')) {
+ return {
+ content: login,
+ addEvent: addLoginButtonEvent,
+ };
+ }
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+
+ const scriptEl = document.getElementById('review-app-toolbar-script')
+ const { projectId, mergeRequestId, mrUrl } = scriptEl.dataset;
+
+ return {
+<<<<<<< HEAD:public/visual-review-toolbar.js
+ href,
+ platform,
+ browser,
+ userAgent,
+ innerWidth,
+ innerHeight,
+ projectId,
+ mergeRequestId,
+ mrUrl,
+=======
+ content: comment,
+ addEvent: () => {
+ addCommentButtonEvent();
+ addLogoutButtonEvent();
+ },
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+ };
+}
+
+function logoutUser() {
+ const { localStorage } = window;
+
+ // All the browsers we support have localStorage, so let's silently fail
+ // and go on with the rest of the functionality.
+ try {
+ localStorage.removeItem('token');
+ } catch (err) {
+ return;
+ }
+
+ addLoginForm();
+}
+
+function postComment({
+ href,
+ platform,
+ browser,
+ userAgent,
+ innerWidth,
+ innerHeight,
+ projectId,
+ mergeRequestId,
+ mrUrl,
+}) {
+ // Clear any old errors
+ clearNote('gitlab-comment');
+
+ setInProgressState();
+
+ const commentText = document.getElementById('gitlab-comment').value.trim();
+
+ if (!commentText) {
+ postError('Your comment appears to be empty.', 'gitlab-comment');
+ resetCommentBox();
+ return;
+ }
+
+ const detailText = `
+<<<<<<< HEAD:public/visual-review-toolbar.js
+ \n
+<details>
+ <summary>Metadata</summary>
+ Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}.
+ <br /><br />
+ <em>User agent: ${userAgent}</em>
+</details>
+=======
+ <details>
+ <summary>Metadata</summary>
+ Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}.
+ <br /><br />
+ *User agent: ${userAgent}*
+ </details>
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+ `;
+
+ const url = `
+ ${mrUrl}/api/v4/projects/${projectId}/merge_requests/${mergeRequestId}/discussions`;
+
+
+ const body = `${commentText} ${detailText}`;
+
+ fetch(url, {
+ method: 'POST',
+ headers: {
+ 'PRIVATE-TOKEN': data.token,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ body }),
+ })
+<<<<<<< HEAD:public/visual-review-toolbar.js
+ .then((response) => {
+ if (response.ok) {
+ confirmAndClear(mergeRequestId);
+ return;
+ }
+
+ throw new Error(`${response.status}: ${response.statusText}`)
+ })
+ .catch((err) => {
+ postError(`The feedback was not sent successfully. Please try again. Error: ${err.message}`, 'gitlab-comment');
+ resetCommentBox();
+ });
+=======
+ .then(response => {
+ if (response.ok) {
+ confirmAndClear(discussionId);
+ return;
+ }
+
+ throw new Error(`${response.status}: ${response.statusText}`);
+ })
+ .catch(err => {
+ postError(
+ `The feedback was not sent successfully. Please try again. Error: ${err.message}`,
+ 'gitlab-comment',
+ );
+ resetCommentBox();
+ });
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+}
+
+function postError(message, inputId) {
+ const note = document.getElementById('gitlab-validation-note');
+ const field = document.getElementById(inputId);
+ field.style.borderColor = '#db3b21';
+ note.style.color = '#db3b21';
+ note.innerText = message;
+}
+
+<<<<<<< HEAD:public/visual-review-toolbar.js
+=======
+function removeButtonAndClickEvent(buttonId, eventListener) {
+ const button = document.getElementById(buttonId);
+ if (button) {
+ button.removeEventListener(eventListener);
+ }
+}
+
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+function resetCommentBox() {
+ const commentBox = document.getElementById('gitlab-comment');
+ const commentButton = document.getElementById('gitlab-comment-button');
+
+ commentButton.innerText = 'Send feedback';
+ commentButton.classList.replace('gitlab-button-secondary', 'gitlab-button-success');
+ commentButton.style.opacity = 1;
+
+ commentBox.style.pointerEvents = 'auto';
+ commentBox.style.color = 'rgba(0, 0, 0, 1)';
+}
+
+function resetCommentButton() {
+ const commentBox = document.getElementById('gitlab-comment');
+ const note = document.getElementById('gitlab-validation-note');
+
+ commentBox.value = '';
+ note.innerText = '';
+ resetCommentBox();
+}
+
+function setInProgressState() {
+ const commentButton = document.getElementById('gitlab-comment-button');
+ const commentBox = document.getElementById('gitlab-comment');
+
+ commentButton.innerText = 'Sending feedback';
+ commentButton.classList.replace('gitlab-button-success', 'gitlab-button-secondary');
+ commentButton.style.opacity = 0.5;
+ commentBox.style.color = 'rgba(223, 223, 223, 0.5)';
+ commentBox.style.pointerEvents = 'none';
+}
+
+function storeToken(token) {
+ const { localStorage } = window;
+
+ // All the browsers we support have localStorage, so let's silently fail
+ // and go on with the rest of the functionality.
+ try {
+ localStorage.setItem('token', token);
+ } catch (err) {
+ return;
+ }
+}
+
+function toggleForm () {
+ const container = document.getElementById('gitlab-review-container');
+ const collapseButton = document.getElementById('gitlab-collapse');
+ const form = document.getElementById('gitlab-form-wrapper');
+ const OPEN = 'open';
+ const CLOSED = 'closed';
+
+ const stateVals = {
+ [OPEN]: {
+ buttonClasses: ['gitlab-collapse-closed', 'gitlab-collapse-open'],
+ containerClasses: ['gitlab-closed-wrapper', 'gitlab-open-wrapper'],
+ icon: compressIcon,
+ display: 'flex',
+ backgroundColor: 'rgba(255, 255, 255, 1)',
+ },
+ [CLOSED]: {
+ buttonClasses: ['gitlab-collapse-open', 'gitlab-collapse-closed'],
+ containerClasses: ['gitlab-open-wrapper', 'gitlab-closed-wrapper'],
+ icon: commentIcon,
+ display: 'none',
+ backgroundColor: 'rgba(255, 255, 255, 0)',
+ },
+ }
+
+<<<<<<< HEAD:public/visual-review-toolbar.js
+ const nextState = collapseButton.classList.contains('gitlab-collapse-open') ? CLOSED : OPEN;
+
+ container.classList.replace(...stateVals[nextState].containerClasses);
+ container.style.backgroundColor = stateVals[nextState].backgroundColor;
+ form.style.display = stateVals[nextState].display;
+ collapseButton.classList.replace(...stateVals[nextState].buttonClasses);
+ collapseButton.innerHTML = stateVals[nextState].icon;
+=======
+ localStorage.setItem('token', token);
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
+}
+
+///////////////////////////////////////////////
+///////////////// INJECTION //////////////////
+///////////////////////////////////////////////
+
+function noop() {};
+
+const eventLookup = ({target: { id }}) => {
+ switch (id) {
+ case 'gitlab-collapse':
+ return toggleForm;
+ case 'gitlab-comment-button':
+ const projectDetails = getProjectDetails();
+ return postComment.bind(null, projectDetails);
+ case 'gitlab-login':
+ return authorizeUser;
+ case 'gitlab-logout-button':
+ return logoutUser;
+ default:
+ return noop;
+ }
+};
+
+window.addEventListener('load', () => {
+ const content = getInitialState();
+ const container = document.createElement('div');
+
+ container.setAttribute('id', 'gitlab-review-container');
+ container.insertAdjacentHTML('beforeend', collapseButton);
+ container.insertAdjacentHTML('beforeend', form(content));
+
+ document.body.insertBefore(container, document.body.firstChild);
+ addStylesheet();
+
+ document.getElementById('gitlab-review-container').addEventListener('click', (event) => {
+ eventLookup(event)();
+ });
+});
diff --git a/config/karma.config.js b/config/karma.config.js
index 2a5bf3581e0..72807798ba2 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -3,7 +3,7 @@ const glob = require('glob');
const chalk = require('chalk');
const webpack = require('webpack');
const argumentsParser = require('commander');
-const webpackConfig = require('./webpack.config.js');
+const webpackConfig = require('./webpack.config.js')[0];
const IS_EE = require('./helpers/is_ee_env');
const ROOT_PATH = path.resolve(__dirname, '..');
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 19b48845305..821e4e5b735 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -102,15 +102,32 @@ if (IS_EE) {
});
}
-module.exports = {
+const baseConfig = {
mode: IS_PRODUCTION ? 'production' : 'development',
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
+ output: {
+ path: path.join(ROOT_PATH, 'public/assets/webpack'),
+ },
+
+ plugins: [
+ // compression can require a lot of compute time and is disabled in CI
+ new CompressionPlugin(),
+ ].filter(Boolean),
+
+ devtool: NO_SOURCEMAPS ? false : devtool,
+};
+
+const mainConfig = {
+ ...baseConfig,
+
+ name: 'main',
+
entry: generateEntries,
output: {
- path: path.join(ROOT_PATH, 'public/assets/webpack'),
+ ...baseConfig.output,
publicPath: '/assets/webpack/',
filename: IS_PRODUCTION ? '[name].[chunkhash:8].bundle.js' : '[name].bundle.js',
chunkFilename: IS_PRODUCTION ? '[name].[chunkhash:8].chunk.js' : '[name].chunk.js',
@@ -236,6 +253,7 @@ module.exports = {
},
plugins: [
+ ...baseConfig.plugins,
// manifest filename must match config.webpack.manifest_filename
// webpack-rails only needs assetsByChunkName to function properly
new StatsWriterPlugin({
@@ -268,7 +286,7 @@ module.exports = {
}),
new webpack.NormalModuleReplacementPlugin(/^ee_component\/(.*)\.vue/, function(resource) {
- if (Object.keys(module.exports.resolve.alias).indexOf('ee') >= 0) {
+ if (Object.keys(module.exports[0].resolve.alias).indexOf('ee') >= 0) {
resource.request = resource.request.replace(/^ee_component/, 'ee');
} else {
resource.request = path.join(
@@ -278,9 +296,6 @@ module.exports = {
}
}),
- // compression can require a lot of compute time and is disabled in CI
- IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
-
// WatchForChangesPlugin
// TODO: publish this as a separate plugin
IS_DEV_SERVER && {
@@ -339,8 +354,37 @@ module.exports = {
inline: DEV_SERVER_LIVERELOAD,
},
- devtool: NO_SOURCEMAPS ? false : devtool,
-
// sqljs requires fs
node: { fs: 'empty' },
};
+
+const visualReviewToolbarConfig = {
+ ...baseConfig,
+
+ name: 'visual_review_toolbar',
+
+ entry: './visual_review_toolbar',
+
+ output: {
+ ...baseConfig.output,
+ filename: 'visual_review_toolbar.js',
+ library: 'VisualReviewToolbar',
+ libraryTarget: 'var',
+ },
+
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ loader: 'babel-loader',
+ options: {
+ cacheDirectory: path.join(CACHE_PATH, 'babel-loader'),
+ },
+ },
+ ],
+ },
+
+ plugins: [...baseConfig.plugins],
+};
+
+module.exports = [mainConfig, visualReviewToolbarConfig];
diff --git a/public/visual-review-toolbar.js b/public/visual-review-toolbar.js
index 6a0fdb29cc2..0a9e497c3cf 100644
--- a/public/visual-review-toolbar.js
+++ b/public/visual-review-toolbar.js
@@ -105,7 +105,7 @@ const linkStyles = `
background-image: none;
`;
-const messageStyles = `
+const messageStyles = `
padding: .25rem 0;
margin: 0;
line-height: 1.2rem;
@@ -281,8 +281,13 @@ const comment = `
`;
const commentIcon = `
+<<<<<<< HEAD:public/visual-review-toolbar.js
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="gitlab-comment-icon"/></svg>
`
+=======
+ <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="a"/></svg>
+`;
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
const compressIcon = `
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/compress</title><path d="M5.27 12.182l-1.562 1.561a1 1 0 0 1-1.414 0h-.001a1 1 0 0 1 0-1.415l1.56-1.56L2.44 9.353a.5.5 0 0 1 .353-.854H7.09a.5.5 0 0 1 .5.5v4.294a.5.5 0 0 1-.853.353l-1.467-1.465zm6.911-6.914l1.464 1.464a.5.5 0 0 1-.353.854H8.999a.5.5 0 0 1-.5-.5V2.793a.5.5 0 0 1 .854-.354l1.414 1.415 1.56-1.561a1 1 0 1 1 1.415 1.414l-1.561 1.56z" id="gitlab-compress-icon"/></svg>
@@ -292,8 +297,7 @@ const collapseButton = `
<button id='gitlab-collapse' style='${buttonClearStyles}' class='gitlab-collapse-open gitlab-button-secondary'>${compressIcon}</button>
`;
-
-const form = (content) => `
+const form = content => `
<div id='gitlab-form-wrapper'>
${content}
</div>
@@ -319,25 +323,94 @@ const login = `
///////////////////////////////////////////////
// from https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator
-function getBrowserId (sUsrAg) {
- var aKeys = ["MSIE", "Edge", "Firefox", "Safari", "Chrome", "Opera"],
- nIdx = aKeys.length - 1;
+function getBrowserId(sUsrAg) {
+ var aKeys = ['MSIE', 'Edge', 'Firefox', 'Safari', 'Chrome', 'Opera'],
+ nIdx = aKeys.length - 1;
for (nIdx; nIdx > -1 && sUsrAg.indexOf(aKeys[nIdx]) === -1; nIdx--);
return aKeys[nIdx];
}
+<<<<<<< HEAD:public/visual-review-toolbar.js
function addCommentForm () {
const formWrapper = document.getElementById('gitlab-form-wrapper');
formWrapper.innerHTML = comment;
+=======
+function addCommentButtonEvent() {
+ // get user agent data
+ const {
+ innerWidth,
+ innerHeight,
+ location: { href },
+ navigator: { platform, userAgent },
+ } = window;
+ const browser = getBrowserId(userAgent);
+
+ const scriptName = 'ReviewAppToolbar';
+ const projectId = document
+ .querySelector(`script[data-name='${scriptName}']`)
+ .getAttribute('data-project');
+ const discussionId = document
+ .querySelector(`script[data-name='${scriptName}']`)
+ .getAttribute('data-discussion');
+ const mrUrl = document
+ .querySelector(`script[data-name='${scriptName}']`)
+ .getAttribute('data-mr-url');
+ const commentButton = document.getElementById('gitlab-comment-button');
+
+ const details = {
+ href,
+ platform,
+ browser,
+ userAgent,
+ innerWidth,
+ innerHeight,
+ projectId,
+ discussionId,
+ mrUrl,
+ };
+
+ commentButton.onclick = postComment.bind(null, details);
+}
+
+function addCollapseEvent() {
+ const collapseButton = document.getElementById('gitlab-collapse');
+ collapseButton.onclick = collapseForm;
+}
+
+function addCommentForm() {
+ const formWrapper = document.getElementById('gitlab-form-wrapper');
+ formWrapper.innerHTML = comment;
+ removeButtonAndClickEvent('gitlab-login', authorizeUser);
+ addCommentButtonEvent();
+ addLogoutButtonEvent();
+}
+
+function addLoginButtonEvent() {
+ const loginButton = document.getElementById('gitlab-login');
+ if (loginButton) {
+ loginButton.onclick = authorizeUser;
+ }
+}
+
+function addLogoutButtonEvent() {
+ const logoutButton = document.getElementById('gitlab-logout-button');
+ if (logoutButton) {
+ logoutButton.onclick = logoutUser;
+ }
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
}
-function addLoginForm () {
+function addLoginForm() {
const formWrapper = document.getElementById('gitlab-form-wrapper');
formWrapper.innerHTML = login;
}
+<<<<<<< HEAD:public/visual-review-toolbar.js
function authorizeUser () {
+=======
+function authorizeUser() {
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
// Clear any old errors
clearNote('gitlab-token');
@@ -357,13 +430,12 @@ function authorizeUser () {
return;
}
-function authSuccess (token) {
+function authSuccess(token) {
data.token = token;
addCommentForm();
}
-
-function clearNote (inputId) {
+function clearNote(inputId) {
const note = document.getElementById('gitlab-validation-note');
note.innerText = '';
note.style.color = '';
@@ -374,7 +446,11 @@ function clearNote (inputId) {
}
}
+<<<<<<< HEAD:public/visual-review-toolbar.js
function confirmAndClear (mergeRequestId) {
+=======
+function confirmAndClear(discussionId) {
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
const commentButton = document.getElementById('gitlab-comment-button');
const note = document.getElementById('gitlab-validation-note');
@@ -384,6 +460,7 @@ function confirmAndClear (mergeRequestId) {
setTimeout(resetCommentButton, 1000);
}
+<<<<<<< HEAD:public/visual-review-toolbar.js
function getInitialState () {
const { localStorage } = window;
@@ -410,11 +487,51 @@ function getProjectDetails () {
platform, userAgent
} } = window;
const browser = getBrowserId(userAgent);
+=======
+function collapseForm() {
+ const container = document.getElementById('gitlab-review-container');
+ const collapseButton = document.getElementById('gitlab-collapse');
+ const form = document.getElementById('gitlab-form-wrapper');
+
+ container.classList.replace('gitlab-open-wrapper', 'gitlab-closed-wrapper');
+ container.style.backgroundColor = 'rgba(255, 255, 255, 0)';
+ form.style.display = 'none';
+
+ collapseButton.classList.replace('gitlab-collapse-open', 'gitlab-collapse-closed');
+ collapseButton.innerHTML = commentIcon;
+ collapseButton.onclick = expandForm;
+}
+
+function expandForm() {
+ const container = document.getElementById('gitlab-review-container');
+ const collapseButton = document.getElementById('gitlab-collapse');
+ const form = document.getElementById('gitlab-form-wrapper');
+
+ container.classList.replace('gitlab-closed-wrapper', 'gitlab-open-wrapper');
+ container.style.backgroundColor = 'rgba(255, 255, 255, 1)';
+ form.style.display = 'flex';
+
+ collapseButton.classList.replace('gitlab-collapse-closed', 'gitlab-collapse-open');
+ collapseButton.innerHTML = compressIcon;
+ collapseButton.onclick = collapseForm;
+}
+
+function getInitialState() {
+ const { localStorage } = window;
+
+ if (!localStorage || !localStorage.getItem('token')) {
+ return {
+ content: login,
+ addEvent: addLoginButtonEvent,
+ };
+ }
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
const scriptEl = document.getElementById('review-app-toolbar-script')
const { projectId, mergeRequestId, mrUrl } = scriptEl.dataset;
return {
+<<<<<<< HEAD:public/visual-review-toolbar.js
href,
platform,
browser,
@@ -424,10 +541,17 @@ function getProjectDetails () {
projectId,
mergeRequestId,
mrUrl,
+=======
+ content: comment,
+ addEvent: () => {
+ addCommentButtonEvent();
+ addLogoutButtonEvent();
+ },
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
};
}
-function logoutUser () {
+function logoutUser() {
const { localStorage } = window;
// All the browsers we support have localStorage, so let's silently fail
@@ -441,7 +565,7 @@ function logoutUser () {
addLoginForm();
}
-function postComment ({
+function postComment({
href,
platform,
browser,
@@ -466,6 +590,7 @@ function postComment ({
}
const detailText = `
+<<<<<<< HEAD:public/visual-review-toolbar.js
\n
<details>
<summary>Metadata</summary>
@@ -473,6 +598,14 @@ function postComment ({
<br /><br />
<em>User agent: ${userAgent}</em>
</details>
+=======
+ <details>
+ <summary>Metadata</summary>
+ Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}.
+ <br /><br />
+ *User agent: ${userAgent}*
+ </details>
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
`;
const url = `
@@ -482,13 +615,14 @@ function postComment ({
const body = `${commentText} ${detailText}`;
fetch(url, {
- method: 'POST',
- headers: {
+ method: 'POST',
+ headers: {
'PRIVATE-TOKEN': data.token,
'Content-Type': 'application/json',
},
body: JSON.stringify({ body }),
})
+<<<<<<< HEAD:public/visual-review-toolbar.js
.then((response) => {
if (response.ok) {
confirmAndClear(mergeRequestId);
@@ -501,9 +635,26 @@ function postComment ({
postError(`The feedback was not sent successfully. Please try again. Error: ${err.message}`, 'gitlab-comment');
resetCommentBox();
});
+=======
+ .then(response => {
+ if (response.ok) {
+ confirmAndClear(discussionId);
+ return;
+ }
+
+ throw new Error(`${response.status}: ${response.statusText}`);
+ })
+ .catch(err => {
+ postError(
+ `The feedback was not sent successfully. Please try again. Error: ${err.message}`,
+ 'gitlab-comment',
+ );
+ resetCommentBox();
+ });
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
}
-function postError (message, inputId) {
+function postError(message, inputId) {
const note = document.getElementById('gitlab-validation-note');
const field = document.getElementById(inputId);
field.style.borderColor = '#db3b21';
@@ -511,6 +662,16 @@ function postError (message, inputId) {
note.innerText = message;
}
+<<<<<<< HEAD:public/visual-review-toolbar.js
+=======
+function removeButtonAndClickEvent(buttonId, eventListener) {
+ const button = document.getElementById(buttonId);
+ if (button) {
+ button.removeEventListener(eventListener);
+ }
+}
+
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
function resetCommentBox() {
const commentBox = document.getElementById('gitlab-comment');
const commentButton = document.getElementById('gitlab-comment-button');
@@ -543,8 +704,7 @@ function setInProgressState() {
commentBox.style.pointerEvents = 'none';
}
-function storeToken (token) {
-
+function storeToken(token) {
const { localStorage } = window;
// All the browsers we support have localStorage, so let's silently fail
@@ -580,6 +740,7 @@ function toggleForm () {
},
}
+<<<<<<< HEAD:public/visual-review-toolbar.js
const nextState = collapseButton.classList.contains('gitlab-collapse-open') ? CLOSED : OPEN;
container.classList.replace(...stateVals[nextState].containerClasses);
@@ -587,6 +748,9 @@ function toggleForm () {
form.style.display = stateVals[nextState].display;
collapseButton.classList.replace(...stateVals[nextState].buttonClasses);
collapseButton.innerHTML = stateVals[nextState].icon;
+=======
+ localStorage.setItem('token', token);
+>>>>>>> a67608fd8b1... Build visual_review_toolbar with webpack:app/assets/javascripts/visual_review_toolbar/index.js
}
///////////////////////////////////////////////