Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
李少辉-开发者
gitlab-foss
提交
e7bc0d7c
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,发现更多精彩内容 >>
提交
e7bc0d7c
编写于
9月 04, 2017
作者:
C
Clement Ho
提交者:
Phil Hughes
9月 04, 2017
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add feature highlight to Issue Boards in new navigation sidebar
上级
970af996
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
735 addition
and
7 deletion
+735
-7
app/assets/javascripts/feature_highlight/feature_highlight.js
...assets/javascripts/feature_highlight/feature_highlight.js
+61
-0
app/assets/javascripts/feature_highlight/feature_highlight_helper.js
...javascripts/feature_highlight/feature_highlight_helper.js
+57
-0
app/assets/javascripts/feature_highlight/feature_highlight_options.js
...avascripts/feature_highlight/feature_highlight_options.js
+12
-0
app/assets/javascripts/main.js
app/assets/javascripts/main.js
+1
-0
app/assets/stylesheets/framework.scss
app/assets/stylesheets/framework.scss
+1
-0
app/assets/stylesheets/framework/buttons.scss
app/assets/stylesheets/framework/buttons.scss
+10
-7
app/assets/stylesheets/framework/feature_highlight.scss
app/assets/stylesheets/framework/feature_highlight.scss
+94
-0
app/views/feature_highlight/_issue_boards.svg
app/views/feature_highlight/_issue_boards.svg
+98
-0
app/views/layouts/nav/_new_project_sidebar.html.haml
app/views/layouts/nav/_new_project_sidebar.html.haml
+14
-0
app/views/shared/icons/_thumbs_up.svg
app/views/shared/icons/_thumbs_up.svg
+1
-0
spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
...cripts/feature_highlight/feature_highlight_helper_spec.js
+219
-0
spec/javascripts/feature_highlight/feature_highlight_options_spec.js
...ripts/feature_highlight/feature_highlight_options_spec.js
+45
-0
spec/javascripts/feature_highlight/feature_highlight_spec.js
spec/javascripts/feature_highlight/feature_highlight_spec.js
+122
-0
未找到文件。
app/assets/javascripts/feature_highlight/feature_highlight.js
0 → 100644
浏览文件 @
e7bc0d7c
import
Cookies
from
'
js-cookie
'
;
import
_
from
'
underscore
'
;
import
{
getCookieName
,
getSelector
,
hidePopover
,
setupDismissButton
,
mouseenter
,
mouseleave
,
}
from
'
./feature_highlight_helper
'
;
export
const
setupFeatureHighlightPopover
=
(
id
,
debounceTimeout
=
300
)
=>
{
const
$selector
=
$
(
getSelector
(
id
));
const
$parent
=
$selector
.
parent
();
const
$popoverContent
=
$parent
.
siblings
(
'
.feature-highlight-popover-content
'
);
const
hideOnScroll
=
hidePopover
.
bind
(
$selector
);
const
debouncedMouseleave
=
_
.
debounce
(
mouseleave
,
debounceTimeout
);
$selector
// Setup popover
.
data
(
'
content
'
,
$popoverContent
.
prop
(
'
outerHTML
'
))
.
popover
({
html
:
true
,
// Override the existing template to add custom CSS classes
template
:
`
<div class="popover feature-highlight-popover" role="tooltip">
<div class="arrow"></div>
<div class="popover-content"></div>
</div>
`
,
})
.
on
(
'
mouseenter
'
,
mouseenter
)
.
on
(
'
mouseleave
'
,
debouncedMouseleave
)
.
on
(
'
inserted.bs.popover
'
,
setupDismissButton
)
.
on
(
'
show.bs.popover
'
,
()
=>
{
window
.
addEventListener
(
'
scroll
'
,
hideOnScroll
);
})
.
on
(
'
hide.bs.popover
'
,
()
=>
{
window
.
removeEventListener
(
'
scroll
'
,
hideOnScroll
);
})
// Display feature highlight
.
removeAttr
(
'
disabled
'
);
};
export
const
shouldHighlightFeature
=
(
id
)
=>
{
const
element
=
document
.
querySelector
(
getSelector
(
id
));
const
previouslyDismissed
=
Cookies
.
get
(
getCookieName
(
id
))
===
'
true
'
;
return
element
&&
!
previouslyDismissed
;
};
export
const
highlightFeatures
=
(
highlightOrder
)
=>
{
const
featureId
=
highlightOrder
.
find
(
shouldHighlightFeature
);
if
(
featureId
)
{
setupFeatureHighlightPopover
(
featureId
);
return
true
;
}
return
false
;
};
app/assets/javascripts/feature_highlight/feature_highlight_helper.js
0 → 100644
浏览文件 @
e7bc0d7c
import
Cookies
from
'
js-cookie
'
;
export
const
getCookieName
=
cookieId
=>
`feature-highlighted-
${
cookieId
}
`
;
export
const
getSelector
=
highlightId
=>
`.js-feature-highlight[data-highlight=
${
highlightId
}
]`
;
export
const
showPopover
=
function
showPopover
()
{
if
(
this
.
hasClass
(
'
js-popover-show
'
))
{
return
false
;
}
this
.
popover
(
'
show
'
);
this
.
addClass
(
'
disable-animation js-popover-show
'
);
return
true
;
};
export
const
hidePopover
=
function
hidePopover
()
{
if
(
!
this
.
hasClass
(
'
js-popover-show
'
))
{
return
false
;
}
this
.
popover
(
'
hide
'
);
this
.
removeClass
(
'
disable-animation js-popover-show
'
);
return
true
;
};
export
const
dismiss
=
function
dismiss
(
cookieId
)
{
Cookies
.
set
(
getCookieName
(
cookieId
),
true
);
hidePopover
.
call
(
this
);
this
.
hide
();
};
export
const
mouseleave
=
function
mouseleave
()
{
if
(
!
$
(
'
.popover:hover
'
).
length
>
0
)
{
const
$featureHighlight
=
$
(
this
);
hidePopover
.
call
(
$featureHighlight
);
}
};
export
const
mouseenter
=
function
mouseenter
()
{
const
$featureHighlight
=
$
(
this
);
const
showedPopover
=
showPopover
.
call
(
$featureHighlight
);
if
(
showedPopover
)
{
$
(
'
.popover
'
)
.
on
(
'
mouseleave
'
,
mouseleave
.
bind
(
$featureHighlight
));
}
};
export
const
setupDismissButton
=
function
setupDismissButton
()
{
const
popoverId
=
this
.
getAttribute
(
'
aria-describedby
'
);
const
cookieId
=
this
.
dataset
.
highlight
;
const
$popover
=
$
(
this
);
const
dismissWrapper
=
dismiss
.
bind
(
$popover
,
cookieId
);
$
(
`#
${
popoverId
}
.dismiss-feature-highlight`
)
.
on
(
'
click
'
,
dismissWrapper
);
};
app/assets/javascripts/feature_highlight/feature_highlight_options.js
0 → 100644
浏览文件 @
e7bc0d7c
import
{
highlightFeatures
}
from
'
./feature_highlight
'
;
import
bp
from
'
../breakpoints
'
;
const
highlightOrder
=
[
'
issue-boards
'
];
export
default
function
domContentLoaded
(
order
)
{
if
(
bp
.
getBreakpointSize
()
===
'
lg
'
)
{
highlightFeatures
(
order
);
}
}
document
.
addEventListener
(
'
DOMContentLoaded
'
,
domContentLoaded
.
bind
(
this
,
highlightOrder
));
app/assets/javascripts/main.js
浏览文件 @
e7bc0d7c
...
...
@@ -102,6 +102,7 @@ import './label_manager';
import
'
./labels
'
;
import
'
./labels_select
'
;
import
'
./layout_nav
'
;
import
'
./feature_highlight/feature_highlight_options
'
;
import
LazyLoader
from
'
./lazy_loader
'
;
import
'
./line_highlighter
'
;
import
'
./logo
'
;
...
...
app/assets/stylesheets/framework.scss
浏览文件 @
e7bc0d7c
...
...
@@ -51,3 +51,4 @@
@import
"framework/snippets"
;
@import
"framework/memory_graph"
;
@import
"framework/responsive-tables"
;
@import
"framework/feature_highlight"
;
app/assets/stylesheets/framework/buttons.scss
浏览文件 @
e7bc0d7c
...
...
@@ -46,6 +46,15 @@
}
}
@mixin
btn-svg
{
svg
{
height
:
15px
;
width
:
15px
;
position
:
relative
;
top
:
2px
;
}
}
@mixin
btn-color
(
$light
,
$border-light
,
$normal
,
$border-normal
,
$dark
,
$border-dark
,
$color
)
{
background-color
:
$light
;
border-color
:
$border-light
;
...
...
@@ -123,6 +132,7 @@
.btn
{
@include
btn-default
;
@include
btn-white
;
@include
btn-svg
;
color
:
$gl-text-color
;
...
...
@@ -222,13 +232,6 @@
}
}
svg
{
height
:
15px
;
width
:
15px
;
position
:
relative
;
top
:
2px
;
}
svg
,
.fa
{
&
:not
(
:last-child
)
{
...
...
app/assets/stylesheets/framework/feature_highlight.scss
0 → 100644
浏览文件 @
e7bc0d7c
.feature-highlight
{
position
:
relative
;
margin-left
:
$gl-padding
;
width
:
20px
;
height
:
20px
;
cursor
:
pointer
;
&
::before
{
content
:
''
;
display
:
block
;
position
:
absolute
;
top
:
6px
;
left
:
6px
;
width
:
8px
;
height
:
8px
;
background-color
:
$blue-500
;
border-radius
:
50%
;
box-shadow
:
0
0
0
rgba
(
$blue-500
,
0
.4
);
animation
:
pulse-highlight
2s
infinite
;
}
&
:hover::before
,
&
.disable-animation
::before
{
animation
:
none
;
}
&
[
disabled
]
::before
{
display
:
none
;
}
}
.is-showing-fly-out
{
.feature-highlight
{
display
:
none
;
}
}
.feature-highlight-popover-content
{
display
:
none
;
hr
{
margin
:
$gl-padding
*
0
.5
0
;
}
.btn-link
{
@include
btn-svg
;
svg
path
{
fill
:
currentColor
;
}
}
.dismiss-feature-highlight
{
padding
:
0
;
}
svg
:first-child
{
width
:
100%
;
background-color
:
$indigo-50
;
border-top-left-radius
:
2px
;
border-top-right-radius
:
2px
;
border-bottom
:
1px
solid
darken
(
$gray-normal
,
8%
);
}
}
.popover
.feature-highlight-popover-content
{
display
:
block
;
}
.feature-highlight-popover
{
padding
:
0
;
.popover-content
{
padding
:
0
;
}
}
.feature-highlight-popover-sub-content
{
padding
:
9px
14px
;
}
@include
keyframes
(
pulse-highlight
)
{
0
%
{
box-shadow
:
0
0
0
0
rgba
(
$blue-200
,
0
.4
);
}
70
%
{
box-shadow
:
0
0
0
10px
transparent
;
}
100
%
{
box-shadow
:
0
0
0
0
transparent
;
}
}
app/views/feature_highlight/_issue_boards.svg
0 → 100644
浏览文件 @
e7bc0d7c
<svg
xmlns=
"http://www.w3.org/2000/svg"
width=
"214"
height=
"102"
viewBox=
"0 0 214 102"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<defs>
<path
id=
"b"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,27 C48,28.1045695 47.1045695,29 46,29 L2,29 C0.8954305,29 1.3527075e-16,28.1045695 0,27 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"a"
width=
"102.1%"
height=
"106.9%"
x=
"-1%"
y=
"-1.7%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
<path
id=
"d"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,26 C48,27.1045695 47.1045695,28 46,28 L2,28 C0.8954305,28 1.3527075e-16,27.1045695 0,26 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"c"
width=
"102.1%"
height=
"107.1%"
x=
"-1%"
y=
"-1.8%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
<path
id=
"e"
d=
"M5,0 L53,0 C55.7614237,-5.07265313e-16 58,2.23857625 58,5 L58,91 C58,93.7614237 55.7614237,96 53,96 L5,96 C2.23857625,96 3.38176876e-16,93.7614237 0,91 L0,5 C-3.38176876e-16,2.23857625 2.23857625,5.07265313e-16 5,0 Z"
/>
<path
id=
"h"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,26 C48,27.1045695 47.1045695,28 46,28 L2,28 C0.8954305,28 1.3527075e-16,27.1045695 0,26 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"g"
width=
"102.1%"
height=
"107.1%"
x=
"-1%"
y=
"-1.8%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
<path
id=
"j"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,26 C48,27.1045695 47.1045695,28 46,28 L2,28 C0.8954305,28 1.3527075e-16,27.1045695 0,26 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"i"
width=
"102.1%"
height=
"107.1%"
x=
"-1%"
y=
"-1.8%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
<path
id=
"l"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,26 C48,27.1045695 47.1045695,28 46,28 L2,28 C0.8954305,28 1.3527075e-16,27.1045695 0,26 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"k"
width=
"102.1%"
height=
"107.1%"
x=
"-1%"
y=
"-1.8%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
<path
id=
"n"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,26 C48,27.1045695 47.1045695,28 46,28 L2,28 C0.8954305,28 1.3527075e-16,27.1045695 0,26 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"m"
width=
"102.1%"
height=
"107.1%"
x=
"-1%"
y=
"-1.8%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
<path
id=
"p"
d=
"M2,0 L46,0 C47.1045695,-2.02906125e-16 48,0.8954305 48,2 L48,26 C48,27.1045695 47.1045695,28 46,28 L2,28 C0.8954305,28 1.3527075e-16,27.1045695 0,26 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z"
/>
<filter
id=
"o"
width=
"102.1%"
height=
"107.1%"
x=
"-1%"
y=
"-1.8%"
filterUnits=
"objectBoundingBox"
>
<feOffset
dy=
"1"
in=
"SourceAlpha"
result=
"shadowOffsetOuter1"
/>
<feColorMatrix
values=
"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"
in=
"shadowOffsetOuter1"
/>
</filter>
</defs>
<g
fill=
"none"
fill-rule=
"evenodd"
>
<path
fill=
"#D6D4DE"
d=
"M14,21 L62,21 C64.7614237,21 67,23.2385763 67,26 L67,112 C67,114.761424 64.7614237,117 62,117 L14,117 C11.2385763,117 9,114.761424 9,112 L9,26 C9,23.2385763 11.2385763,21 14,21 Z"
/>
<g
transform=
"translate(11 23)"
>
<path
fill=
"#FFFFFF"
d=
"M5,0 L53,0 C55.7614237,-5.07265313e-16 58,2.23857625 58,5 L58,91 C58,93.7614237 55.7614237,96 53,96 L5,96 C2.23857625,96 3.38176876e-16,93.7614237 0,91 L0,5 C-3.38176876e-16,2.23857625 2.23857625,5.07265313e-16 5,0 Z"
/>
<path
fill=
"#FC6D26"
d=
"M4,0 L54,0 C56.209139,-4.05812251e-16 58,1.790861 58,4 L0,4 C-2.705415e-16,1.790861 1.790861,4.05812251e-16 4,0 Z"
/>
<g
transform=
"translate(5 10)"
>
<use
fill=
"black"
filter=
"url(#a)"
xlink:href=
"#b"
/>
<use
fill=
"#F9F9F9"
xlink:href=
"#b"
/>
</g>
<g
transform=
"translate(5 42)"
>
<use
fill=
"black"
filter=
"url(#c)"
xlink:href=
"#d"
/>
<use
fill=
"#FEF0E8"
xlink:href=
"#d"
/>
<path
fill=
"#FEE1D3"
d=
"M9,8 L33,8 C34.1045695,8 35,8.8954305 35,10 C35,11.1045695 34.1045695,12 33,12 L9,12 C7.8954305,12 7,11.1045695 7,10 C7,8.8954305 7.8954305,8 9,8 Z"
/>
<path
fill=
"#FDC4A8"
d=
"M9,17 L17,17 C18.1045695,17 19,17.8954305 19,19 C19,20.1045695 18.1045695,21 17,21 L9,21 C7.8954305,21 7,20.1045695 7,19 C7,17.8954305 7.8954305,17 9,17 Z"
/>
<path
fill=
"#FC6D26"
d=
"M24,17 L32,17 C33.1045695,17 34,17.8954305 34,19 C34,20.1045695 33.1045695,21 32,21 L24,21 C22.8954305,21 22,20.1045695 22,19 C22,17.8954305 22.8954305,17 24,17 Z"
/>
</g>
</g>
<path
fill=
"#D6D4DE"
d=
"M148,26 L196,26 C198.761424,26 201,28.2385763 201,31 L201,117 C201,119.761424 198.761424,122 196,122 L148,122 C145.238576,122 143,119.761424 143,117 L143,31 C143,28.2385763 145.238576,26 148,26 Z"
/>
<g
transform=
"translate(145 28)"
>
<mask
id=
"f"
fill=
"white"
>
<use
xlink:href=
"#e"
/>
</mask>
<use
fill=
"#FFFFFF"
xlink:href=
"#e"
/>
<path
fill=
"#FC6D26"
d=
"M4,0 L54,0 C56.209139,-4.05812251e-16 58,1.790861 58,4 L0,4 C-2.705415e-16,1.790861 1.790861,4.05812251e-16 4,0 Z"
mask=
"url(#f)"
/>
<g
transform=
"translate(5 10)"
>
<use
fill=
"black"
filter=
"url(#g)"
xlink:href=
"#h"
/>
<use
fill=
"#F9F9F9"
xlink:href=
"#h"
/>
</g>
<g
transform=
"translate(5 42)"
>
<use
fill=
"black"
filter=
"url(#i)"
xlink:href=
"#j"
/>
<use
fill=
"#FEF0E8"
xlink:href=
"#j"
/>
<path
fill=
"#FEE1D3"
d=
"M9 8L33 8C34.1045695 8 35 8.8954305 35 10 35 11.1045695 34.1045695 12 33 12L9 12C7.8954305 12 7 11.1045695 7 10 7 8.8954305 7.8954305 8 9 8zM9 17L13 17C14.1045695 17 15 17.8954305 15 19 15 20.1045695 14.1045695 21 13 21L9 21C7.8954305 21 7 20.1045695 7 19 7 17.8954305 7.8954305 17 9 17z"
/>
<path
fill=
"#FC6D26"
d=
"M20,17 L24,17 C25.1045695,17 26,17.8954305 26,19 C26,20.1045695 25.1045695,21 24,21 L20,21 C18.8954305,21 18,20.1045695 18,19 C18,17.8954305 18.8954305,17 20,17 Z"
/>
<path
fill=
"#FDC4A8"
d=
"M31,17 L35,17 C36.1045695,17 37,17.8954305 37,19 C37,20.1045695 36.1045695,21 35,21 L31,21 C29.8954305,21 29,20.1045695 29,19 C29,17.8954305 29.8954305,17 31,17 Z"
/>
</g>
</g>
<path
fill=
"#D6D4DE"
d=
"M81,14 L129,14 C131.761424,14 134,16.2385763 134,19 L134,105 C134,107.761424 131.761424,110 129,110 L81,110 C78.2385763,110 76,107.761424 76,105 L76,19 C76,16.2385763 78.2385763,14 81,14 Z"
/>
<g
transform=
"translate(78 16)"
>
<path
fill=
"#FFFFFF"
d=
"M5,0 L53,0 C55.7614237,-5.07265313e-16 58,2.23857625 58,5 L58,91 C58,93.7614237 55.7614237,96 53,96 L5,96 C2.23857625,96 3.38176876e-16,93.7614237 0,91 L0,5 C-3.38176876e-16,2.23857625 2.23857625,5.07265313e-16 5,0 Z"
/>
<g
transform=
"translate(5 10)"
>
<use
fill=
"black"
filter=
"url(#k)"
xlink:href=
"#l"
/>
<use
fill=
"#EFEDF8"
xlink:href=
"#l"
/>
<path
fill=
"#E1DBF1"
d=
"M9,8 L33,8 C34.1045695,8 35,8.8954305 35,10 C35,11.1045695 34.1045695,12 33,12 L9,12 C7.8954305,12 7,11.1045695 7,10 C7,8.8954305 7.8954305,8 9,8 Z"
/>
<path
fill=
"#6B4FBB"
d=
"M9,17 L13,17 C14.1045695,17 15,17.8954305 15,19 C15,20.1045695 14.1045695,21 13,21 L9,21 C7.8954305,21 7,20.1045695 7,19 C7,17.8954305 7.8954305,17 9,17 Z"
/>
<path
fill=
"#C3B8E3"
d=
"M20,17 L28,17 C29.1045695,17 30,17.8954305 30,19 C30,20.1045695 29.1045695,21 28,21 L20,21 C18.8954305,21 18,20.1045695 18,19 C18,17.8954305 18.8954305,17 20,17 Z"
/>
</g>
<g
transform=
"translate(5 42)"
>
<use
fill=
"black"
filter=
"url(#m)"
xlink:href=
"#n"
/>
<use
fill=
"#F9F9F9"
xlink:href=
"#n"
/>
</g>
<g
transform=
"translate(5 74)"
>
<rect
width=
"34"
height=
"4"
x=
"7"
y=
"7"
fill=
"#E1DBF1"
rx=
"2"
/>
<use
fill=
"black"
filter=
"url(#o)"
xlink:href=
"#p"
/>
<use
fill=
"#F9F9F9"
xlink:href=
"#p"
/>
</g>
<path
fill=
"#6B4FBB"
d=
"M4,0 L54,0 C56.209139,-4.05812251e-16 58,1.790861 58,4 L0,4 C-2.705415e-16,1.790861 1.790861,4.05812251e-16 4,0 Z"
/>
</g>
</g>
</svg>
app/views/layouts/nav/_new_project_sidebar.html.haml
浏览文件 @
e7bc0d7c
...
...
@@ -99,6 +99,20 @@
=
link_to
project_boards_path
(
@project
),
title:
'Board'
do
%span
Board
.feature-highlight.js-feature-highlight
{
disabled:
true
,
data:
{
trigger:
'manual'
,
container:
'body'
,
toggle:
'popover'
,
placement:
'right'
,
highlight:
'issue-boards'
}
}
.feature-highlight-popover-content
=
render
'feature_highlight/issue_boards.svg'
.feature-highlight-popover-sub-content
%span
=
_
(
'Use'
)
=
link_to
'Issue Boards'
,
project_boards_path
(
@project
)
%span
=
_
(
'to create customized software development workflows like'
)
%strong
=
_
(
'Scrum'
)
%span
=
_
(
'or'
)
%strong
=
_
(
'Kanban'
)
%hr
%button
.btn-link.dismiss-feature-highlight
{
type:
'button'
}
%span
=
_
(
"Got it! Don't show this again"
)
=
custom_icon
(
'thumbs_up'
)
=
nav_link
(
controller: :labels
)
do
=
link_to
project_labels_path
(
@project
),
title:
'Labels'
do
...
...
app/views/shared/icons/_thumbs_up.svg
0 → 100644
浏览文件 @
e7bc0d7c
<svg
xmlns=
"http://www.w3.org/2000/svg"
width=
"16"
height=
"16"
viewBox=
"0 0 16 16"
><path
fill-rule=
"evenodd"
d=
"M8.33 5h5.282a2 2 0 0 1 1.963 2.38l-.563 2.905a3 3 0 0 1-.243.732l-1.104 2.286A3 3 0 0 1 10.964 15H7a3 3 0 0 1-3-3V5.7a2 2 0 0 1 .436-1.247l3.11-3.9A.632.632 0 0 1 8.486.5l.138.137a1 1 0 0 1 .28.87L8.33 5zM1 6h2v7H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1z"
/></svg>
spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
0 → 100644
浏览文件 @
e7bc0d7c
import
Cookies
from
'
js-cookie
'
;
import
{
getCookieName
,
getSelector
,
showPopover
,
hidePopover
,
dismiss
,
mouseleave
,
mouseenter
,
setupDismissButton
,
}
from
'
~/feature_highlight/feature_highlight_helper
'
;
describe
(
'
feature highlight helper
'
,
()
=>
{
describe
(
'
getCookieName
'
,
()
=>
{
it
(
'
returns `feature-highlighted-` prefix
'
,
()
=>
{
const
cookieId
=
'
cookieId
'
;
expect
(
getCookieName
(
cookieId
)).
toEqual
(
`feature-highlighted-
${
cookieId
}
`
);
});
});
describe
(
'
getSelector
'
,
()
=>
{
it
(
'
returns js-feature-highlight selector
'
,
()
=>
{
const
highlightId
=
'
highlightId
'
;
expect
(
getSelector
(
highlightId
)).
toEqual
(
`.js-feature-highlight[data-highlight=
${
highlightId
}
]`
);
});
});
describe
(
'
showPopover
'
,
()
=>
{
it
(
'
returns true when popover is shown
'
,
()
=>
{
const
context
=
{
hasClass
:
()
=>
false
,
popover
:
()
=>
{},
addClass
:
()
=>
{},
};
expect
(
showPopover
.
call
(
context
)).
toEqual
(
true
);
});
it
(
'
returns false when popover is already shown
'
,
()
=>
{
const
context
=
{
hasClass
:
()
=>
true
,
};
expect
(
showPopover
.
call
(
context
)).
toEqual
(
false
);
});
it
(
'
shows popover
'
,
(
done
)
=>
{
const
context
=
{
hasClass
:
()
=>
false
,
popover
:
()
=>
{},
addClass
:
()
=>
{},
};
spyOn
(
context
,
'
popover
'
).
and
.
callFake
((
method
)
=>
{
expect
(
method
).
toEqual
(
'
show
'
);
done
();
});
showPopover
.
call
(
context
);
});
it
(
'
adds disable-animation and js-popover-show class
'
,
(
done
)
=>
{
const
context
=
{
hasClass
:
()
=>
false
,
popover
:
()
=>
{},
addClass
:
()
=>
{},
};
spyOn
(
context
,
'
addClass
'
).
and
.
callFake
((
classNames
)
=>
{
expect
(
classNames
).
toEqual
(
'
disable-animation js-popover-show
'
);
done
();
});
showPopover
.
call
(
context
);
});
});
describe
(
'
hidePopover
'
,
()
=>
{
it
(
'
returns true when popover is hidden
'
,
()
=>
{
const
context
=
{
hasClass
:
()
=>
true
,
popover
:
()
=>
{},
removeClass
:
()
=>
{},
};
expect
(
hidePopover
.
call
(
context
)).
toEqual
(
true
);
});
it
(
'
returns false when popover is already hidden
'
,
()
=>
{
const
context
=
{
hasClass
:
()
=>
false
,
};
expect
(
hidePopover
.
call
(
context
)).
toEqual
(
false
);
});
it
(
'
hides popover
'
,
(
done
)
=>
{
const
context
=
{
hasClass
:
()
=>
true
,
popover
:
()
=>
{},
removeClass
:
()
=>
{},
};
spyOn
(
context
,
'
popover
'
).
and
.
callFake
((
method
)
=>
{
expect
(
method
).
toEqual
(
'
hide
'
);
done
();
});
hidePopover
.
call
(
context
);
});
it
(
'
removes disable-animation and js-popover-show class
'
,
(
done
)
=>
{
const
context
=
{
hasClass
:
()
=>
true
,
popover
:
()
=>
{},
removeClass
:
()
=>
{},
};
spyOn
(
context
,
'
removeClass
'
).
and
.
callFake
((
classNames
)
=>
{
expect
(
classNames
).
toEqual
(
'
disable-animation js-popover-show
'
);
done
();
});
hidePopover
.
call
(
context
);
});
});
describe
(
'
dismiss
'
,
()
=>
{
const
context
=
{
hide
:
()
=>
{},
};
beforeEach
(()
=>
{
spyOn
(
Cookies
,
'
set
'
).
and
.
callFake
(()
=>
{});
spyOn
(
hidePopover
,
'
call
'
).
and
.
callFake
(()
=>
{});
spyOn
(
context
,
'
hide
'
).
and
.
callFake
(()
=>
{});
dismiss
.
call
(
context
);
});
it
(
'
sets cookie to true
'
,
()
=>
{
expect
(
Cookies
.
set
).
toHaveBeenCalled
();
});
it
(
'
calls hide popover
'
,
()
=>
{
expect
(
hidePopover
.
call
).
toHaveBeenCalled
();
});
it
(
'
calls hide
'
,
()
=>
{
expect
(
context
.
hide
).
toHaveBeenCalled
();
});
});
describe
(
'
mouseleave
'
,
()
=>
{
it
(
'
calls hide popover if .popover:hover is false
'
,
()
=>
{
const
fakeJquery
=
{
length
:
0
,
};
spyOn
(
$
.
fn
,
'
init
'
).
and
.
callFake
(
selector
=>
(
selector
===
'
.popover:hover
'
?
fakeJquery
:
$
.
fn
));
spyOn
(
hidePopover
,
'
call
'
);
mouseleave
();
expect
(
hidePopover
.
call
).
toHaveBeenCalled
();
});
it
(
'
does not call hide popover if .popover:hover is true
'
,
()
=>
{
const
fakeJquery
=
{
length
:
1
,
};
spyOn
(
$
.
fn
,
'
init
'
).
and
.
callFake
(
selector
=>
(
selector
===
'
.popover:hover
'
?
fakeJquery
:
$
.
fn
));
spyOn
(
hidePopover
,
'
call
'
);
mouseleave
();
expect
(
hidePopover
.
call
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'
mouseenter
'
,
()
=>
{
const
context
=
{};
it
(
'
shows popover
'
,
()
=>
{
spyOn
(
showPopover
,
'
call
'
).
and
.
returnValue
(
false
);
mouseenter
.
call
(
context
);
expect
(
showPopover
.
call
).
toHaveBeenCalled
();
});
it
(
'
registers mouseleave event if popover is showed
'
,
(
done
)
=>
{
spyOn
(
showPopover
,
'
call
'
).
and
.
returnValue
(
true
);
spyOn
(
$
.
fn
,
'
on
'
).
and
.
callFake
((
eventName
)
=>
{
expect
(
eventName
).
toEqual
(
'
mouseleave
'
);
done
();
});
mouseenter
.
call
(
context
);
});
it
(
'
does not register mouseleave event if popover is not showed
'
,
()
=>
{
spyOn
(
showPopover
,
'
call
'
).
and
.
returnValue
(
false
);
const
spy
=
spyOn
(
$
.
fn
,
'
on
'
).
and
.
callFake
(()
=>
{});
mouseenter
.
call
(
context
);
expect
(
spy
).
not
.
toHaveBeenCalled
();
});
});
describe
(
'
setupDismissButton
'
,
()
=>
{
it
(
'
registers click event callback
'
,
(
done
)
=>
{
const
context
=
{
getAttribute
:
()
=>
'
popoverId
'
,
dataset
:
{
highlight
:
'
cookieId
'
,
},
};
spyOn
(
$
.
fn
,
'
on
'
).
and
.
callFake
((
event
)
=>
{
expect
(
event
).
toEqual
(
'
click
'
);
done
();
});
setupDismissButton
.
call
(
context
);
});
});
});
spec/javascripts/feature_highlight/feature_highlight_options_spec.js
0 → 100644
浏览文件 @
e7bc0d7c
import
domContentLoaded
from
'
~/feature_highlight/feature_highlight_options
'
;
import
bp
from
'
~/breakpoints
'
;
describe
(
'
feature highlight options
'
,
()
=>
{
describe
(
'
domContentLoaded
'
,
()
=>
{
const
highlightOrder
=
[];
beforeEach
(()
=>
{
// Check for when highlightFeatures is called
spyOn
(
highlightOrder
,
'
find
'
).
and
.
callFake
(()
=>
{});
});
it
(
'
should not call highlightFeatures when breakpoint is xs
'
,
()
=>
{
spyOn
(
bp
,
'
getBreakpointSize
'
).
and
.
returnValue
(
'
xs
'
);
domContentLoaded
(
highlightOrder
);
expect
(
bp
.
getBreakpointSize
).
toHaveBeenCalled
();
expect
(
highlightOrder
.
find
).
not
.
toHaveBeenCalled
();
});
it
(
'
should not call highlightFeatures when breakpoint is sm
'
,
()
=>
{
spyOn
(
bp
,
'
getBreakpointSize
'
).
and
.
returnValue
(
'
sm
'
);
domContentLoaded
(
highlightOrder
);
expect
(
bp
.
getBreakpointSize
).
toHaveBeenCalled
();
expect
(
highlightOrder
.
find
).
not
.
toHaveBeenCalled
();
});
it
(
'
should not call highlightFeatures when breakpoint is md
'
,
()
=>
{
spyOn
(
bp
,
'
getBreakpointSize
'
).
and
.
returnValue
(
'
md
'
);
domContentLoaded
(
highlightOrder
);
expect
(
bp
.
getBreakpointSize
).
toHaveBeenCalled
();
expect
(
highlightOrder
.
find
).
not
.
toHaveBeenCalled
();
});
it
(
'
should call highlightFeatures when breakpoint is lg
'
,
()
=>
{
spyOn
(
bp
,
'
getBreakpointSize
'
).
and
.
returnValue
(
'
lg
'
);
domContentLoaded
(
highlightOrder
);
expect
(
bp
.
getBreakpointSize
).
toHaveBeenCalled
();
expect
(
highlightOrder
.
find
).
toHaveBeenCalled
();
});
});
});
spec/javascripts/feature_highlight/feature_highlight_spec.js
0 → 100644
浏览文件 @
e7bc0d7c
import
Cookies
from
'
js-cookie
'
;
import
*
as
featureHighlightHelper
from
'
~/feature_highlight/feature_highlight_helper
'
;
import
*
as
featureHighlight
from
'
~/feature_highlight/feature_highlight
'
;
describe
(
'
feature highlight
'
,
()
=>
{
describe
(
'
setupFeatureHighlightPopover
'
,
()
=>
{
const
selector
=
'
.js-feature-highlight[data-highlight=test]
'
;
beforeEach
(()
=>
{
setFixtures
(
`
<div>
<div class="js-feature-highlight" data-highlight="test" disabled>
Trigger
</div>
</div>
<div class="feature-highlight-popover-content">
Content
<div class="dismiss-feature-highlight">
Dismiss
</div>
</div>
`
);
spyOn
(
window
,
'
addEventListener
'
);
spyOn
(
window
,
'
removeEventListener
'
);
featureHighlight
.
setupFeatureHighlightPopover
(
'
test
'
,
0
);
});
it
(
'
setups popover content
'
,
()
=>
{
const
$popoverContent
=
$
(
'
.feature-highlight-popover-content
'
);
const
outerHTML
=
$popoverContent
.
prop
(
'
outerHTML
'
);
expect
(
$
(
selector
).
data
(
'
content
'
)).
toEqual
(
outerHTML
);
});
it
(
'
setups mouseenter
'
,
()
=>
{
const
showSpy
=
spyOn
(
featureHighlightHelper
.
showPopover
,
'
call
'
);
$
(
selector
).
trigger
(
'
mouseenter
'
);
expect
(
showSpy
).
toHaveBeenCalled
();
});
it
(
'
setups debounced mouseleave
'
,
(
done
)
=>
{
const
hideSpy
=
spyOn
(
featureHighlightHelper
.
hidePopover
,
'
call
'
);
$
(
selector
).
trigger
(
'
mouseleave
'
);
// Even though we've set the debounce to 0ms, setTimeout is needed for the debounce
setTimeout
(()
=>
{
expect
(
hideSpy
).
toHaveBeenCalled
();
done
();
},
0
);
});
it
(
'
setups inserted.bs.popover
'
,
()
=>
{
$
(
selector
).
trigger
(
'
mouseenter
'
);
const
popoverId
=
$
(
selector
).
attr
(
'
aria-describedby
'
);
const
spyEvent
=
spyOnEvent
(
`#
${
popoverId
}
.dismiss-feature-highlight`
,
'
click
'
);
$
(
`#
${
popoverId
}
.dismiss-feature-highlight`
).
click
();
expect
(
spyEvent
).
toHaveBeenTriggered
();
});
it
(
'
setups show.bs.popover
'
,
()
=>
{
$
(
selector
).
trigger
(
'
show.bs.popover
'
);
expect
(
window
.
addEventListener
).
toHaveBeenCalledWith
(
'
scroll
'
,
jasmine
.
any
(
Function
));
});
it
(
'
setups hide.bs.popover
'
,
()
=>
{
$
(
selector
).
trigger
(
'
hide.bs.popover
'
);
expect
(
window
.
removeEventListener
).
toHaveBeenCalledWith
(
'
scroll
'
,
jasmine
.
any
(
Function
));
});
it
(
'
removes disabled attribute
'
,
()
=>
{
expect
(
$
(
'
.js-feature-highlight
'
).
is
(
'
:disabled
'
)).
toEqual
(
false
);
});
it
(
'
displays popover
'
,
()
=>
{
expect
(
$
(
selector
).
attr
(
'
aria-describedby
'
)).
toBeFalsy
();
$
(
selector
).
trigger
(
'
mouseenter
'
);
expect
(
$
(
selector
).
attr
(
'
aria-describedby
'
)).
toBeTruthy
();
});
});
describe
(
'
shouldHighlightFeature
'
,
()
=>
{
it
(
'
should return false if element is not found
'
,
()
=>
{
spyOn
(
document
,
'
querySelector
'
).
and
.
returnValue
(
null
);
spyOn
(
Cookies
,
'
get
'
).
and
.
returnValue
(
null
);
expect
(
featureHighlight
.
shouldHighlightFeature
()).
toBeFalsy
();
});
it
(
'
should return false if previouslyDismissed
'
,
()
=>
{
spyOn
(
document
,
'
querySelector
'
).
and
.
returnValue
(
document
.
createElement
(
'
div
'
));
spyOn
(
Cookies
,
'
get
'
).
and
.
returnValue
(
'
true
'
);
expect
(
featureHighlight
.
shouldHighlightFeature
()).
toBeFalsy
();
});
it
(
'
should return true if element is found and not previouslyDismissed
'
,
()
=>
{
spyOn
(
document
,
'
querySelector
'
).
and
.
returnValue
(
document
.
createElement
(
'
div
'
));
spyOn
(
Cookies
,
'
get
'
).
and
.
returnValue
(
null
);
expect
(
featureHighlight
.
shouldHighlightFeature
()).
toBeTruthy
();
});
});
describe
(
'
highlightFeatures
'
,
()
=>
{
it
(
'
calls setupFeatureHighlightPopover if shouldHighlightFeature returns true
'
,
()
=>
{
// Mimic shouldHighlightFeature set to true
const
highlightOrder
=
[
'
issue-boards
'
];
spyOn
(
highlightOrder
,
'
find
'
).
and
.
returnValue
(
highlightOrder
[
0
]);
expect
(
featureHighlight
.
highlightFeatures
(
highlightOrder
)).
toEqual
(
true
);
});
it
(
'
does not call setupFeatureHighlightPopover if shouldHighlightFeature returns false
'
,
()
=>
{
// Mimic shouldHighlightFeature set to false
const
highlightOrder
=
[
'
issue-boards
'
];
spyOn
(
highlightOrder
,
'
find
'
).
and
.
returnValue
(
null
);
expect
(
featureHighlight
.
highlightFeatures
(
highlightOrder
)).
toEqual
(
false
);
});
});
});
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录