summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2017-05-16 10:37:05 +0100
committerPhil Hughes <me@iamphill.com>2017-05-16 10:37:10 +0100
commit86ab9edbb2836feb3eb52fe859f1bc9521787fc2 (patch)
treebe902c3844b2ebd97b284ab8bdfff492809bf91a /app/assets
parent96a46521002f17aff2fc09f02778432ae049c6ee (diff)
parent5f2b142b66ed8f355b36b91907ca9bea17c070f2 (diff)
downloadgitlab-ce-86ab9edbb2836feb3eb52fe859f1bc9521787fc2.tar.gz
Merge branch 'issue-edit-inline' into issue-edit-inline-description-field
[ci skip]
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js10
-rw-r--r--app/assets/javascripts/filtered_search/stores/recent_searches_store.js1
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue52
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue78
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue11
-rw-r--r--app/assets/javascripts/issue_show/components/fields/title.vue9
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue44
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue36
-rw-r--r--app/assets/javascripts/issue_show/services/index.js6
-rw-r--r--app/assets/javascripts/notes.js8
-rw-r--r--app/assets/stylesheets/framework/nav.scss22
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss1
-rw-r--r--app/assets/stylesheets/pages/builds.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss12
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss23
-rw-r--r--app/assets/stylesheets/pages/notes.scss9
16 files changed, 175 insertions, 149 deletions
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 9fea563370f..57d247e11a9 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -16,10 +16,14 @@ class FilteredSearchManager {
this.recentSearchesStore = new RecentSearchesStore({
isLocalStorageAvailable: RecentSearchesService.isAvailable(),
});
- let recentSearchesKey = 'issue-recent-searches';
+ const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
+ const projectPath = searchHistoryDropdownElement ?
+ searchHistoryDropdownElement.dataset.projectFullPath : 'project';
+ let recentSearchesPagePrefix = 'issue-recent-searches';
if (page === 'merge_requests') {
- recentSearchesKey = 'merge-request-recent-searches';
+ recentSearchesPagePrefix = 'merge-request-recent-searches';
}
+ const recentSearchesKey = `${projectPath}-${recentSearchesPagePrefix}`;
this.recentSearchesService = new RecentSearchesService(recentSearchesKey);
// Fetch recent searches from localStorage
@@ -47,7 +51,7 @@ class FilteredSearchManager {
this.recentSearchesRoot = new RecentSearchesRoot(
this.recentSearchesStore,
this.recentSearchesService,
- document.querySelector('.js-filtered-search-history-dropdown'),
+ searchHistoryDropdownElement,
);
this.recentSearchesRoot.init();
diff --git a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
index 066be69766a..35fc15e4c87 100644
--- a/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
+++ b/app/assets/javascripts/filtered_search/stores/recent_searches_store.js
@@ -3,6 +3,7 @@ import _ from 'underscore';
class RecentSearchesStore {
constructor(initialState = {}) {
this.state = Object.assign({
+ isLocalStorageAvailable: true,
recentSearches: [],
}, initialState);
}
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index 6ec66da0ed4..eb594cfb60b 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -7,7 +7,7 @@ import Service from '../services/index';
import Store from '../stores';
import titleComponent from './title.vue';
import descriptionComponent from './description.vue';
-import editActions from './edit_actions.vue';
+import formComponent from './form.vue';
export default {
props: {
@@ -60,19 +60,18 @@ export default {
return {
store,
state: store.state,
- formState: store.formState,
showForm: false,
};
},
computed: {
- elementType() {
- return this.showForm ? 'form' : 'div';
+ formState() {
+ return this.store.formState;
},
},
components: {
descriptionComponent,
titleComponent,
- editActions,
+ formComponent,
},
methods: {
openForm() {
@@ -82,8 +81,11 @@ export default {
description: this.state.descriptionText,
};
},
+ closeForm() {
+ this.showForm = false;
+ },
updateIssuable() {
- this.service.updateIssuable(this.formState)
+ this.service.updateIssuable(this.store.formState)
.then(() => {
eventHub.$emit('close.form');
})
@@ -134,32 +136,38 @@ export default {
eventHub.$on('delete.issuable', this.deleteIssuable);
eventHub.$on('update.issuable', this.updateIssuable);
+ eventHub.$on('close.form', this.closeForm);
eventHub.$on('open.form', this.openForm);
},
beforeDestroy() {
eventHub.$off('delete.issuable', this.deleteIssuable);
eventHub.$off('update.issuable', this.updateIssuable);
- eventHub.$on('open.form', this.openForm);
+ eventHub.$off('close.form', this.closeForm);
+ eventHub.$off('open.form', this.openForm);
},
};
</script>
<template>
- <div :is="elementType">
- <title-component
- :store="store"
- :show-form="showForm"
- :issuable-ref="issuableRef"
- :title-html="state.titleHtml"
- :title-text="state.titleText" />
- <description-component
- :store="store"
- :show-form="showForm"
- :can-update="canUpdate"
- :markdown-preview-url="markdownPreviewUrl"
- :markdown-docs="markdownDocs" />
- <edit-actions
+ <div>
+ <form-component
v-if="canUpdate && showForm"
- :can-destroy="canDestroy" />
+ :form-state="formState"
+ :can-destroy="canDestroy"
+ :markdown-docs="markdownDocs"
+ :markdown-preview-url="markdownPreviewUrl" />
+ <div v-else>
+ <title-component
+ :issuable-ref="issuableRef"
+ :title-html="state.titleHtml"
+ :title-text="state.titleText" />
+ <description-component
+ v-if="state.descriptionHtml"
+ :can-update="canUpdate"
+ :description-html="state.descriptionHtml"
+ :description-text="state.descriptionText"
+ :updated-at="state.updatedAt"
+ :task-status="state.taskStatus" />
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index fb542c70489..fc73d5db899 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -1,6 +1,5 @@
<script>
import animateMixin from '../mixins/animate';
- import descriptionField from './fields/description.vue';
export default {
mixins: [animateMixin],
@@ -9,45 +8,32 @@
type: Boolean,
required: true,
},
- store: {
- type: Object,
+ descriptionHtml: {
+ type: String,
required: true,
},
- showForm: {
- type: Boolean,
+ descriptionText: {
+ type: String,
required: true,
},
- markdownPreviewUrl: {
+ updatedAt: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
- markdownDocs: {
+ taskStatus: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
},
data() {
return {
- state: this.store.state,
preAnimation: false,
pulseAnimation: false,
timeAgoEl: $('.js-issue-edited-ago'),
};
},
- computed: {
- descriptionHtml() {
- return this.state.descriptionHtml;
- },
- descriptionText() {
- return this.state.descriptionText;
- },
- updatedAt() {
- return this.state.updated_at;
- },
- taskStatus() {
- return this.state.taskStatus;
- },
- },
watch: {
descriptionHtml() {
this.animateChange();
@@ -91,9 +77,6 @@
}
},
},
- components: {
- descriptionField,
- },
mounted() {
this.renderGFM();
},
@@ -101,32 +84,25 @@
</script>
<template>
- <div :class="{ 'common-note-form': showForm }">
- <description-field
- v-if="showForm"
- :store="store"
- :markdown-preview-url="markdownPreviewUrl"
- :markdown-docs="markdownDocs" />
+ <div
+ v-else-if="descriptionHtml"
+ class="description"
+ :class="{
+ 'js-task-list-container': canUpdate
+ }">
<div
- v-else-if="descriptionHtml"
- class="description"
+ class="wiki"
:class="{
- 'js-task-list-container': canUpdate
- }">
- <div
- class="wiki"
- :class="{
- 'issue-realtime-pre-pulse': preAnimation,
- 'issue-realtime-trigger-pulse': pulseAnimation
- }"
- v-html="descriptionHtml"
- ref="gfm-content">
- </div>
- <textarea
- class="hidden js-task-list-field"
- v-if="descriptionText"
- v-model="descriptionText">
- </textarea>
+ 'issue-realtime-pre-pulse': preAnimation,
+ 'issue-realtime-trigger-pulse': pulseAnimation
+ }"
+ v-html="descriptionHtml"
+ ref="gfm-content">
</div>
+ <textarea
+ class="hidden js-task-list-field"
+ v-if="descriptionText"
+ v-model="descriptionText">
+ </textarea>
</div>
</template>
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index bade7634e72..b4c31811a0b 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -4,7 +4,7 @@
export default {
props: {
- store: {
+ formState: {
type: Object,
required: true,
},
@@ -17,11 +17,6 @@
required: true,
},
},
- data() {
- return {
- state: this.store.formState,
- };
- },
components: {
markdownField,
},
@@ -29,7 +24,7 @@
</script>
<template>
- <div>
+ <div class="common-note-form">
<label
class="sr-only"
for="issue-description">
@@ -43,7 +38,7 @@
class="note-textarea js-gfm-input js-autosize markdown-area"
data-supports-slash-commands="false"
aria-label="Description"
- v-model="state.description"
+ v-model="formState.description"
ref="textatea"
slot="textarea">
</textarea>
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
index 732a4021205..01ae6fd3dd2 100644
--- a/app/assets/javascripts/issue_show/components/fields/title.vue
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -1,16 +1,11 @@
<script>
export default {
props: {
- store: {
+ formState: {
type: Object,
required: true,
},
},
- data() {
- return {
- state: this.store.formState,
- };
- },
};
</script>
@@ -27,6 +22,6 @@
type="text"
placeholder="Issue title"
aria-label="Issue title"
- v-model="state.title" />
+ v-model="formState.title" />
</fieldset>
</template>
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
new file mode 100644
index 00000000000..7732a1c194a
--- /dev/null
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -0,0 +1,44 @@
+<script>
+ import titleField from './fields/title.vue';
+ import descriptionField from './fields/description.vue';
+ import editActions from './edit_actions.vue';
+
+ export default {
+ props: {
+ canDestroy: {
+ type: Boolean,
+ required: true,
+ },
+ formState: {
+ type: Object,
+ required: true,
+ },
+ markdownPreviewUrl: {
+ type: String,
+ required: true,
+ },
+ markdownDocs: {
+ type: String,
+ required: true,
+ },
+ },
+ components: {
+ titleField,
+ descriptionField,
+ editActions,
+ },
+ };
+</script>
+
+<template>
+ <form>
+ <title-field
+ :form-state="formState" />
+ <description-field
+ :form-state="formState"
+ :markdown-preview-url="markdownPreviewUrl"
+ :markdown-docs="markdownDocs" />
+ <edit-actions
+ :can-destroy="canDestroy" />
+ </form>
+</template>
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index a61ce414891..a9dabd4cff1 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -1,12 +1,8 @@
<script>
import animateMixin from '../mixins/animate';
- import titleField from './fields/title.vue';
export default {
mixins: [animateMixin],
- components: {
- titleField,
- },
data() {
return {
preAnimation: false,
@@ -27,14 +23,6 @@
type: String,
required: true,
},
- store: {
- type: Object,
- required: true,
- },
- showForm: {
- type: Boolean,
- required: true,
- },
},
watch: {
titleHtml() {
@@ -53,19 +41,13 @@
</script>
<template>
- <div>
- <title-field
- v-if="showForm"
- :store="store" />
- <h2
- v-else
- class="title"
- :class="{
- 'issue-realtime-pre-pulse': preAnimation,
- 'issue-realtime-trigger-pulse': pulseAnimation
- }"
- v-html="titleHtml"
- >
- </h2>
- </div>
+ <h2
+ class="title"
+ :class="{
+ 'issue-realtime-pre-pulse': preAnimation,
+ 'issue-realtime-trigger-pulse': pulseAnimation
+ }"
+ v-html="titleHtml"
+ >
+ </h2>
</template>
diff --git a/app/assets/javascripts/issue_show/services/index.js b/app/assets/javascripts/issue_show/services/index.js
index 5da15882c25..0ceff34cf8b 100644
--- a/app/assets/javascripts/issue_show/services/index.js
+++ b/app/assets/javascripts/issue_show/services/index.js
@@ -8,15 +8,15 @@ export default class Service {
this.endpoint = endpoint;
this.resource = Vue.resource(this.endpoint, {}, {
- rendered_title: {
+ realtimeChanges: {
method: 'GET',
- url: `${this.endpoint}/rendered_title`,
+ url: `${this.endpoint}/realtime_changes`,
},
});
}
getData() {
- return this.resource.rendered_title();
+ return this.resource.realtimeChanges();
}
deleteIssuable() {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 7ba44835741..f143bfbfc29 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -24,7 +24,7 @@ const normalizeNewlines = function(str) {
(function() {
this.Notes = (function() {
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
- const REGEX_SLASH_COMMANDS = /^\/\w+/gm;
+ const REGEX_SLASH_COMMANDS = /^\/\w+.*$/gm;
Notes.interval = null;
@@ -1170,6 +1170,7 @@ const normalizeNewlines = function(str) {
*/
Notes.prototype.createPlaceholderNote = function({ formContent, uniqueId, isDiscussionNote, currentUsername, currentUserFullname }) {
const discussionClass = isDiscussionNote ? 'discussion' : '';
+ const escapedFormContent = _.escape(formContent);
const $tempNote = $(
`<li id="${uniqueId}" class="note being-posted fade-in-half timeline-entry">
<div class="timeline-entry-inner">
@@ -1183,14 +1184,11 @@ const normalizeNewlines = function(str) {
<span class="hidden-xs">${currentUserFullname}</span>
<span class="note-headline-light">@${currentUsername}</span>
</a>
- <span class="note-headline-light">
- <i class="fa fa-spinner fa-spin" aria-label="Comment is being posted" aria-hidden="true"></i>
- </span>
</div>
</div>
<div class="note-body">
<div class="note-text">
- <p>${formContent}</p>
+ <p>${escapedFormContent}</p>
</div>
</div>
</div>
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 64e6ab391b6..d2f6a227128 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -24,10 +24,10 @@
}
@mixin scrolling-links() {
- white-space: nowrap;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
+ display: flex;
&::-webkit-scrollbar {
display: none;
@@ -35,6 +35,7 @@
}
.nav-links {
+ display: flex;
padding: 0;
margin: 0;
list-style: none;
@@ -42,17 +43,16 @@
border-bottom: 1px solid $border-color;
li {
- display: inline-block;
+ display: flex;
a {
- display: inline-block;
padding: $gl-btn-padding;
padding-bottom: 11px;
- margin-bottom: -1px;
font-size: 14px;
line-height: 28px;
color: $gl-text-color-secondary;
border-bottom: 2px solid transparent;
+ white-space: nowrap;
&:hover,
&:active,
@@ -85,10 +85,10 @@
.container-fluid {
background-color: $gray-normal;
margin-bottom: 0;
+ display: flex;
}
li {
-
&.active a {
border-bottom: none;
color: $link-underline-blue;
@@ -137,9 +137,9 @@
}
.nav-links {
- display: inline-block;
margin-bottom: 0;
border-bottom: none;
+ float: left;
&.wide {
width: 100%;
@@ -337,6 +337,10 @@
border-bottom: none;
height: 51px;
+ @media (min-width: $screen-sm-min) {
+ justify-content: center;
+ }
+
li {
a {
padding-top: 10px;
@@ -347,6 +351,7 @@
.scrolling-tabs-container {
position: relative;
+ overflow: hidden;
.nav-links {
@include scrolling-links();
@@ -484,10 +489,7 @@
.inner-page-scroll-tabs {
position: relative;
-
- .nav-links {
- padding-bottom: 1px;
- }
+ overflow: hidden;
.fade-right {
@include fade(left, $white-light);
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 2b5ab539955..018f61ca3a8 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -53,6 +53,7 @@
.right-sidebar-expanded {
padding-right: 0;
+ z-index: 300;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
&:not(.wiki-sidebar):not(.build-sidebar) .content-wrapper {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 724b4080ee0..14a62b6cbf0 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -378,7 +378,7 @@
background-color: $row-hover;
}
- .fa-spinner {
+ .fa-refresh {
font-size: 13px;
margin-left: 3px;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index c4210ffd823..0184208ab82 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -23,16 +23,6 @@
.merge-manually {
@extend .fixed-width-container;
}
-
- .merge-request-tabs-holder {
- &.affix {
- border-bottom: 1px solid $border-color;
-
- .nav-links {
- border: 0;
- }
- }
- }
}
.merge-request-details {
@@ -206,7 +196,7 @@
transition: width .3s;
background: $gray-light;
padding: 10px 20px;
- z-index: 2;
+ z-index: 200;
&.right-sidebar-expanded {
width: $gutter_width;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 0173a05b403..d208e54207e 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -121,6 +121,7 @@
.dropdown-menu {
margin-top: 11px;
+ z-index: 200;
}
.ci-action-icon-wrapper {
@@ -690,8 +691,9 @@
.merge-request-tabs-holder {
top: $header-height;
- z-index: 10;
+ z-index: 100;
background-color: $white-light;
+ border-bottom: 1px solid $border-color;
@media(min-width: $screen-sm-min) {
position: sticky;
@@ -711,6 +713,16 @@
padding-right: $gl-padding;
}
}
+
+ .nav-links {
+ border: 0;
+ }
+}
+
+.merge-request-tabs {
+ display: flex;
+ margin-bottom: 0;
+ padding: 0;
}
.limit-container-width {
@@ -721,6 +733,15 @@
}
}
+.merge-request-tabs-container {
+ display: flex;
+ justify-content: space-between;
+
+ @media (max-width: $screen-xs-max) {
+ flex-direction: column-reverse;
+ }
+}
+
.limit-container-width:not(.container-limited) {
.merge-request-tabs-holder:not(.affix) {
.merge-request-tabs-container {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 0600bb1cb1a..5b6aa9d74f6 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -609,6 +609,15 @@ ul.notes {
}
.line-resolve-all-container {
+ @media (min-width: $screen-sm-min) {
+ margin-right: 0;
+ padding-left: $gl-padding;
+ }
+
+ > div {
+ white-space: nowrap;
+ }
+
.btn-group {
margin-left: -4px;
}