Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
李少辉-开发者
gitlab-foss
提交
dd18faff
G
gitlab-foss
项目概览
李少辉-开发者
/
gitlab-foss
通知
15
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gitlab-foss
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
dd18faff
编写于
4月 05, 2017
作者:
F
Filipa Lacerda
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'issue-boards-list-template-in-js' into 'master'
Issue boards list template in JS file See merge request !9958
上级
6831ee8f
6718c188
变更
6
隐藏空白更改
内联
并排
Showing
6 changed file
with
382 addition
and
140 deletion
+382
-140
app/assets/javascripts/boards/components/board.js
app/assets/javascripts/boards/components/board.js
+2
-2
app/assets/javascripts/boards/components/board_list.js
app/assets/javascripts/boards/components/board_list.js
+176
-110
app/assets/javascripts/boards/components/board_new_issue.js
app/assets/javascripts/boards/components/board_new_issue.js
+3
-1
app/views/projects/boards/_show.html.haml
app/views/projects/boards/_show.html.haml
+0
-1
app/views/projects/boards/components/_board_list.html.haml
app/views/projects/boards/components/_board_list.html.haml
+0
-26
spec/javascripts/boards/board_list_spec.js
spec/javascripts/boards/board_list_spec.js
+201
-0
未找到文件。
app/assets/javascripts/boards/components/board.js
浏览文件 @
dd18faff
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
/* global Sortable */
import
Vue
from
'
vue
'
;
import
boardList
from
'
./board_list
'
;
import
boardBlankState
from
'
./board_blank_state
'
;
require
(
'
./board_delete
'
);
...
...
@@ -16,7 +16,7 @@ require('./board_list');
gl
.
issueBoards
.
Board
=
Vue
.
extend
({
template
:
'
#js-board-template
'
,
components
:
{
'
board-list
'
:
gl
.
issueBoards
.
B
oardList
,
b
oardList
,
'
board-delete
'
:
gl
.
issueBoards
.
BoardDelete
,
boardBlankState
,
},
...
...
app/assets/javascripts/boards/components/board_list.js
浏览文件 @
dd18faff
/* eslint-disable comma-dangle, space-before-function-paren, max-len */
/* global Sortable */
import
Vue
from
'
vue
'
;
import
boardNewIssue
from
'
./board_new_issue
'
;
import
boardCard
from
'
./board_card
'
;
import
eventHub
from
'
../eventhub
'
;
(()
=>
{
const
Store
=
gl
.
issueBoards
.
BoardsStore
;
window
.
gl
=
window
.
gl
||
{};
window
.
gl
.
issueBoards
=
window
.
gl
.
issueBoards
||
{};
const
Store
=
gl
.
issueBoards
.
BoardsStore
;
gl
.
issueBoards
.
BoardList
=
Vue
.
extend
({
template
:
'
#js-board-list-template
'
,
components
:
{
boardCard
,
boardNewIssue
,
export
default
{
name
:
'
BoardList
'
,
props
:
{
disabled
:
{
type
:
Boolean
,
required
:
true
,
},
props
:
{
disabled
:
Boolean
,
list
:
Object
,
issues
:
Array
,
loading
:
Boolean
,
issueLinkBase
:
String
,
rootPath
:
String
,
list
:
{
type
:
Object
,
required
:
true
,
},
data
()
{
return
{
scrollOffset
:
250
,
filters
:
Store
.
state
.
filters
,
showCount
:
false
,
showIssueForm
:
false
};
issues
:
{
type
:
Array
,
required
:
true
,
},
watch
:
{
filters
:
{
handler
()
{
this
.
list
.
loadingMore
=
false
;
this
.
$refs
.
list
.
scrollTop
=
0
;
},
deep
:
true
},
issues
()
{
this
.
$nextTick
(()
=>
{
if
(
this
.
scrollHeight
()
<=
this
.
listHeight
()
&&
this
.
list
.
issuesSize
>
this
.
list
.
issues
.
length
)
{
this
.
list
.
page
+=
1
;
this
.
list
.
getIssues
(
false
);
}
loading
:
{
type
:
Boolean
,
required
:
true
,
},
issueLinkBase
:
{
type
:
String
,
required
:
true
,
},
rootPath
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
scrollOffset
:
250
,
filters
:
Store
.
state
.
filters
,
showCount
:
false
,
showIssueForm
:
false
,
};
},
components
:
{
boardCard
,
boardNewIssue
,
},
methods
:
{
listHeight
()
{
return
this
.
$refs
.
list
.
getBoundingClientRect
().
height
;
},
scrollHeight
()
{
return
this
.
$refs
.
list
.
scrollHeight
;
},
scrollTop
()
{
return
this
.
$refs
.
list
.
scrollTop
+
this
.
listHeight
();
},
loadNextPage
()
{
const
getIssues
=
this
.
list
.
nextPage
();
if
(
this
.
scrollHeight
()
>
Math
.
ceil
(
this
.
listHeight
()))
{
this
.
showCount
=
true
;
}
else
{
this
.
showCount
=
false
;
}
if
(
getIssues
)
{
this
.
list
.
loadingMore
=
true
;
getIssues
.
then
(()
=>
{
this
.
list
.
loadingMore
=
false
;
});
}
},
methods
:
{
listHeight
()
{
return
this
.
$refs
.
list
.
getBoundingClientRect
().
height
;
},
scrollHeight
()
{
return
this
.
$refs
.
list
.
scrollHeight
;
},
scrollTop
()
{
return
this
.
$refs
.
list
.
scrollTop
+
this
.
listHeight
();
toggleForm
()
{
this
.
showIssueForm
=
!
this
.
showIssueForm
;
},
onScroll
()
{
if
((
this
.
scrollTop
()
>
this
.
scrollHeight
()
-
this
.
scrollOffset
)
&&
!
this
.
list
.
loadingMore
)
{
this
.
loadNextPage
();
}
},
},
watch
:
{
filters
:
{
handler
()
{
this
.
list
.
loadingMore
=
false
;
this
.
$refs
.
list
.
scrollTop
=
0
;
},
loadNextPage
()
{
const
getIssues
=
this
.
list
.
nextPage
();
deep
:
true
,
},
issues
()
{
this
.
$nextTick
(()
=>
{
if
(
this
.
scrollHeight
()
<=
this
.
listHeight
()
&&
this
.
list
.
issuesSize
>
this
.
list
.
issues
.
length
)
{
this
.
list
.
page
+=
1
;
this
.
list
.
getIssues
(
false
);
}
if
(
getIssues
)
{
this
.
list
.
loadingMore
=
true
;
getIssues
.
then
(()
=>
{
this
.
list
.
loadingMore
=
false
;
});
if
(
this
.
scrollHeight
()
>
Math
.
ceil
(
this
.
listHeight
()))
{
this
.
showCount
=
true
;
}
else
{
this
.
showCount
=
false
;
}
},
toggleForm
()
{
this
.
showIssueForm
=
!
this
.
showIssueForm
;
},
},
created
()
{
gl
.
IssueBoardsApp
.
$on
(
`hide-issue-form-
${
this
.
list
.
id
}
`
,
this
.
toggleForm
);
});
},
mounted
()
{
const
options
=
gl
.
issueBoards
.
getBoardSortableDefaultOptions
({
scroll
:
document
.
querySelectorAll
(
'
.boards-list
'
)[
0
],
group
:
'
issues
'
,
disabled
:
this
.
disabled
,
filter
:
'
.board-list-count, .is-disabled
'
,
dataIdAttr
:
'
data-issue-id
'
,
onStart
:
(
e
)
=>
{
const
card
=
this
.
$refs
.
issue
[
e
.
oldIndex
];
},
created
()
{
eventHub
.
$on
(
`hide-issue-form-
${
this
.
list
.
id
}
`
,
this
.
toggleForm
);
},
mounted
()
{
const
options
=
gl
.
issueBoards
.
getBoardSortableDefaultOptions
({
scroll
:
document
.
querySelectorAll
(
'
.boards-list
'
)[
0
],
group
:
'
issues
'
,
disabled
:
this
.
disabled
,
filter
:
'
.board-list-count, .is-disabled
'
,
dataIdAttr
:
'
data-issue-id
'
,
onStart
:
(
e
)
=>
{
const
card
=
this
.
$refs
.
issue
[
e
.
oldIndex
];
card
.
showDetail
=
false
;
Store
.
moving
.
list
=
card
.
list
;
Store
.
moving
.
issue
=
Store
.
moving
.
list
.
findIssue
(
+
e
.
item
.
dataset
.
issueId
);
card
.
showDetail
=
false
;
Store
.
moving
.
list
=
card
.
list
;
Store
.
moving
.
issue
=
Store
.
moving
.
list
.
findIssue
(
+
e
.
item
.
dataset
.
issueId
);
gl
.
issueBoards
.
onStart
();
},
onAdd
:
(
e
)
=>
{
gl
.
issueBoards
.
BoardsStore
.
moveIssueToList
(
Store
.
moving
.
list
,
this
.
list
,
Store
.
moving
.
issue
,
e
.
newIndex
);
gl
.
issueBoards
.
onStart
();
},
onAdd
:
(
e
)
=>
{
gl
.
issueBoards
.
BoardsStore
.
moveIssueToList
(
Store
.
moving
.
list
,
this
.
list
,
Store
.
moving
.
issue
,
e
.
newIndex
);
this
.
$nextTick
(()
=>
{
e
.
item
.
remove
();
});
},
onUpdate
:
(
e
)
=>
{
const
sortedArray
=
this
.
sortable
.
toArray
().
filter
(
id
=>
id
!==
'
-1
'
);
gl
.
issueBoards
.
BoardsStore
.
moveIssueInList
(
this
.
list
,
Store
.
moving
.
issue
,
e
.
oldIndex
,
e
.
newIndex
,
sortedArray
);
},
onMove
(
e
)
{
return
!
e
.
related
.
classList
.
contains
(
'
board-list-count
'
);
}
});
this
.
$nextTick
(()
=>
{
e
.
item
.
remove
();
});
},
onUpdate
:
(
e
)
=>
{
const
sortedArray
=
this
.
sortable
.
toArray
().
filter
(
id
=>
id
!==
'
-1
'
);
gl
.
issueBoards
.
BoardsStore
.
moveIssueInList
(
this
.
list
,
Store
.
moving
.
issue
,
e
.
oldIndex
,
e
.
newIndex
,
sortedArray
);
},
onMove
(
e
)
{
return
!
e
.
related
.
classList
.
contains
(
'
board-list-count
'
);
},
});
this
.
sortable
=
Sortable
.
create
(
this
.
$refs
.
list
,
options
);
this
.
sortable
=
Sortable
.
create
(
this
.
$refs
.
list
,
options
);
// Scroll event on list to load more
this
.
$refs
.
list
.
onscroll
=
()
=>
{
if
((
this
.
scrollTop
()
>
this
.
scrollHeight
()
-
this
.
scrollOffset
)
&&
!
this
.
list
.
loadingMore
)
{
this
.
loadNextPage
();
}
};
},
beforeDestroy
()
{
gl
.
IssueBoardsApp
.
$off
(
`hide-issue-form-
${
this
.
list
.
id
}
`
,
this
.
toggleForm
);
},
});
})();
// Scroll event on list to load more
this
.
$refs
.
list
.
addEventListener
(
'
scroll
'
,
this
.
onScroll
);
},
beforeDestroy
()
{
eventHub
.
$off
(
`hide-issue-form-
${
this
.
list
.
id
}
`
,
this
.
toggleForm
);
this
.
$refs
.
list
.
removeEventListener
(
'
scroll
'
,
this
.
onScroll
);
},
template
:
`
<div class="board-list-component">
<div
class="board-list-loading text-center"
aria-label="Loading issues"
v-if="loading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true">
</i>
</div>
<board-new-issue
:list="list"
v-if="list.type !== 'closed' && showIssueForm"/>
<ul
class="board-list"
v-show="!loading"
ref="list"
:data-board="list.id"
:class="{ 'is-smaller': showIssueForm }">
<board-card
v-for="(issue, index) in issues"
ref="issue"
:index="index"
:list="list"
:issue="issue"
:issue-link-base="issueLinkBase"
:root-path="rootPath"
:disabled="disabled"
:key="issue.id" />
<li
class="board-list-count text-center"
v-if="showCount"
data-id="-1">
<i
class="fa fa-spinner fa-spin"
aria-label="Loading more issues"
aria-hidden="true"
v-show="list.loadingMore">
</i>
<span v-if="list.issues.length === list.issuesSize">
Showing all issues
</span>
<span v-else>
Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
</span>
</li>
</ul>
</div>
`
,
};
app/assets/javascripts/boards/components/board_new_issue.js
浏览文件 @
dd18faff
/* global ListIssue */
import
eventHub
from
'
../eventhub
'
;
const
Store
=
gl
.
issueBoards
.
BoardsStore
;
export
default
{
...
...
@@ -49,7 +51,7 @@ export default {
},
cancel
()
{
this
.
title
=
''
;
gl
.
IssueBoardsApp
.
$emit
(
`hide-issue-form-
${
this
.
list
.
id
}
`
);
eventHub
.
$emit
(
`hide-issue-form-
${
this
.
list
.
id
}
`
);
},
},
mounted
()
{
...
...
app/views/projects/boards/_show.html.haml
浏览文件 @
dd18faff
...
...
@@ -8,7 +8,6 @@
=
page_specific_javascript_bundle_tag
(
'boards'
)
%script
#js-board-template
{
type:
"text/x-template"
}=
render
"projects/boards/components/board"
%script
#js-board-list-template
{
type:
"text/x-template"
}=
render
"projects/boards/components/board_list"
%script
#js-board-modal-filter
{
type:
"text/x-template"
}=
render
"shared/issuable/search_bar"
,
type: :boards_modal
=
render
"projects/issues/head"
...
...
app/views/projects/boards/components/_board_list.html.haml
已删除
100644 → 0
浏览文件 @
6831ee8f
.board-list-component
.board-list-loading.text-center
{
"v-if"
=>
"loading"
}
=
icon
(
"spinner spin"
)
-
if
can?
current_user
,
:create_issue
,
@project
%board-new-issue
{
":list"
=>
"list"
,
"v-if"
=>
'list.type !== "closed" && showIssueForm'
}
%ul
.board-list
{
"ref"
=>
"list"
,
"v-show"
=>
"!loading"
,
":data-board"
=>
"list.id"
,
":class"
=>
'
{
"is-smaller"
:
showIssueForm
}
'
}
%board-card
{
"v-for"
=>
"(issue, index) in issues"
,
"ref"
=>
"issue"
,
":index"
=>
"index"
,
":list"
=>
"list"
,
":issue"
=>
"issue"
,
":issue-link-base"
=>
"issueLinkBase"
,
":root-path"
=>
"rootPath"
,
":disabled"
=>
"disabled"
,
":key"
=>
"issue.id"
}
%li
.board-list-count.text-center
{
"v-if"
=>
"showCount"
,
"data-issue-id"
=>
"-1"
}
=
icon
(
"spinner spin"
,
"v-show"
=>
"list.loadingMore"
)
%span
{
"v-if"
=>
"list.issues.length === list.issuesSize"
}
Showing all issues
%span
{
"v-else"
=>
true
}
Showing {{ list.issues.length }} of {{ list.issuesSize }} issues
spec/javascripts/boards/board_list_spec.js
0 → 100644
浏览文件 @
dd18faff
/* global BoardService */
/* global boardsMockInterceptor */
/* global List */
/* global listObj */
/* global ListIssue */
import
Vue
from
'
vue
'
;
import
_
from
'
underscore
'
;
import
Sortable
from
'
vendor/Sortable
'
;
import
BoardList
from
'
~/boards/components/board_list
'
;
import
eventHub
from
'
~/boards/eventhub
'
;
import
'
~/boards/mixins/sortable_default_options
'
;
import
'
~/boards/models/issue
'
;
import
'
~/boards/models/list
'
;
import
'
~/boards/stores/boards_store
'
;
import
'
./mock_data
'
;
window
.
Sortable
=
Sortable
;
describe
(
'
Board list component
'
,
()
=>
{
let
component
;
beforeEach
((
done
)
=>
{
const
el
=
document
.
createElement
(
'
div
'
);
document
.
body
.
appendChild
(
el
);
Vue
.
http
.
interceptors
.
push
(
boardsMockInterceptor
);
gl
.
boardService
=
new
BoardService
(
'
/test/issue-boards/board
'
,
''
,
'
1
'
);
gl
.
issueBoards
.
BoardsStore
.
create
();
gl
.
IssueBoardsApp
=
new
Vue
();
const
BoardListComp
=
Vue
.
extend
(
BoardList
);
const
list
=
new
List
(
listObj
);
const
issue
=
new
ListIssue
({
title
:
'
Testing
'
,
iid
:
1
,
confidential
:
false
,
labels
:
[],
});
list
.
issuesSize
=
1
;
list
.
issues
.
push
(
issue
);
component
=
new
BoardListComp
({
el
,
propsData
:
{
disabled
:
false
,
list
,
issues
:
list
.
issues
,
loading
:
false
,
issueLinkBase
:
'
/issues
'
,
rootPath
:
'
/
'
,
},
}).
$mount
();
Vue
.
nextTick
(()
=>
{
done
();
});
});
afterEach
(()
=>
{
Vue
.
http
.
interceptors
=
_
.
without
(
Vue
.
http
.
interceptors
,
boardsMockInterceptor
);
});
it
(
'
renders component
'
,
()
=>
{
expect
(
component
.
$el
.
classList
.
contains
(
'
board-list-component
'
),
).
toBe
(
true
);
});
it
(
'
renders loading icon
'
,
(
done
)
=>
{
component
.
loading
=
true
;
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-loading
'
),
).
not
.
toBeNull
();
done
();
});
});
it
(
'
renders issues
'
,
()
=>
{
expect
(
component
.
$el
.
querySelectorAll
(
'
.card
'
).
length
,
).
toBe
(
1
);
});
it
(
'
sets data attribute with issue id
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.card
'
).
getAttribute
(
'
data-issue-id
'
),
).
toBe
(
'
1
'
);
});
it
(
'
shows new issue form
'
,
(
done
)
=>
{
component
.
toggleForm
();
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-new-issue-form
'
),
).
not
.
toBeNull
();
expect
(
component
.
$el
.
querySelector
(
'
.is-smaller
'
),
).
not
.
toBeNull
();
done
();
});
});
it
(
'
shows new issue form after eventhub event
'
,
(
done
)
=>
{
eventHub
.
$emit
(
`hide-issue-form-
${
component
.
list
.
id
}
`
);
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-new-issue-form
'
),
).
not
.
toBeNull
();
expect
(
component
.
$el
.
querySelector
(
'
.is-smaller
'
),
).
not
.
toBeNull
();
done
();
});
});
it
(
'
does not show new issue form for closed list
'
,
(
done
)
=>
{
component
.
list
.
type
=
'
closed
'
;
component
.
toggleForm
();
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-new-issue-form
'
),
).
toBeNull
();
done
();
});
});
it
(
'
shows count list item
'
,
(
done
)
=>
{
component
.
showCount
=
true
;
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
),
).
not
.
toBeNull
();
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
).
textContent
.
trim
(),
).
toBe
(
'
Showing all issues
'
);
done
();
});
});
it
(
'
shows how many more issues to load
'
,
(
done
)
=>
{
component
.
showCount
=
true
;
component
.
list
.
issuesSize
=
20
;
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count
'
).
textContent
.
trim
(),
).
toBe
(
'
Showing 1 of 20 issues
'
);
done
();
});
});
it
(
'
loads more issues after scrolling
'
,
(
done
)
=>
{
spyOn
(
component
.
list
,
'
nextPage
'
);
component
.
$refs
.
list
.
style
.
height
=
'
100px
'
;
component
.
$refs
.
list
.
style
.
overflow
=
'
scroll
'
;
for
(
let
i
=
0
;
i
<
19
;
i
+=
1
)
{
const
issue
=
component
.
list
.
issues
[
0
];
issue
.
id
+=
1
;
component
.
list
.
issues
.
push
(
issue
);
}
Vue
.
nextTick
(()
=>
{
component
.
$refs
.
list
.
scrollTop
=
20000
;
setTimeout
(()
=>
{
expect
(
component
.
list
.
nextPage
).
toHaveBeenCalled
();
done
();
});
});
});
it
(
'
shows loading more spinner
'
,
(
done
)
=>
{
component
.
showCount
=
true
;
component
.
list
.
loadingMore
=
true
;
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-list-count .fa-spinner
'
),
).
not
.
toBeNull
();
done
();
});
});
});
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录