Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
c0242485
Commit
c0242485
authored
Feb 22, 2017
by
Eric Eastwood
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update permalink/blame buttons with line number fragment hash
Fix
https://gitlab.com/gitlab-org/gitlab-ce/issues/19742
parent
1d4b11f3
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
157 additions
and
28 deletions
+157
-28
app/assets/javascripts/blob/blob_line_permalink_updater.js
app/assets/javascripts/blob/blob_line_permalink_updater.js
+35
-0
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+8
-0
app/assets/javascripts/line_highlighter.js
app/assets/javascripts/line_highlighter.js
+1
-11
app/assets/stylesheets/framework/highlight.scss
app/assets/stylesheets/framework/highlight.scss
+7
-2
app/views/projects/blob/_actions.html.haml
app/views/projects/blob/_actions.html.haml
+1
-1
changelogs/unreleased/19742-permalink-blame-button-line-number-hash-links.yml
...d/19742-permalink-blame-button-line-number-hash-links.yml
+4
-0
spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
...atures/projects/blobs/blob_line_permalink_updater_spec.rb
+97
-0
spec/javascripts/line_highlighter_spec.js
spec/javascripts/line_highlighter_spec.js
+4
-14
No files found.
app/assets/javascripts/blob/blob_line_permalink_updater.js
0 → 100644
View file @
c0242485
const
lineNumberRe
=
/^L
[
0-9
]
+/
;
const
updateLineNumbersOnBlobPermalinks
=
(
linksToUpdate
)
=>
{
const
hash
=
gl
.
utils
.
getLocationHash
();
if
(
hash
&&
lineNumberRe
.
test
(
hash
))
{
const
hashUrlString
=
`#
${
hash
}
`
;
[].
concat
(
Array
.
prototype
.
slice
.
call
(
linksToUpdate
)).
forEach
((
permalinkButton
)
=>
{
const
baseHref
=
permalinkButton
.
getAttribute
(
'
data-original-href
'
)
||
(()
=>
{
const
href
=
permalinkButton
.
getAttribute
(
'
href
'
);
permalinkButton
.
setAttribute
(
'
data-original-href
'
,
href
);
return
href
;
})();
permalinkButton
.
setAttribute
(
'
href
'
,
`
${
baseHref
}${
hashUrlString
}
`
);
});
}
};
function
BlobLinePermalinkUpdater
(
blobContentHolder
,
lineNumberSelector
,
elementsToUpdate
)
{
const
updateBlameAndBlobPermalinkCb
=
()
=>
{
// Wait for the hash to update from the LineHighlighter callback
setTimeout
(()
=>
{
updateLineNumbersOnBlobPermalinks
(
elementsToUpdate
);
},
0
);
};
blobContentHolder
.
addEventListener
(
'
click
'
,
(
e
)
=>
{
if
(
e
.
target
.
matches
(
lineNumberSelector
))
{
updateBlameAndBlobPermalinkCb
();
}
});
updateBlameAndBlobPermalinkCb
();
}
export
default
BlobLinePermalinkUpdater
;
app/assets/javascripts/dispatcher.js
View file @
c0242485
...
@@ -40,6 +40,7 @@ import BindInOut from './behaviors/bind_in_out';
...
@@ -40,6 +40,7 @@ import BindInOut from './behaviors/bind_in_out';
import
GroupsList
from
'
./groups_list
'
;
import
GroupsList
from
'
./groups_list
'
;
import
ProjectsList
from
'
./projects_list
'
;
import
ProjectsList
from
'
./projects_list
'
;
import
MiniPipelineGraph
from
'
./mini_pipeline_graph_dropdown
'
;
import
MiniPipelineGraph
from
'
./mini_pipeline_graph_dropdown
'
;
import
BlobLinePermalinkUpdater
from
'
./blob/blob_line_permalink_updater
'
;
const
ShortcutsBlob
=
require
(
'
./shortcuts_blob
'
);
const
ShortcutsBlob
=
require
(
'
./shortcuts_blob
'
);
const
UserCallout
=
require
(
'
./user_callout
'
);
const
UserCallout
=
require
(
'
./user_callout
'
);
...
@@ -252,6 +253,13 @@ const UserCallout = require('./user_callout');
...
@@ -252,6 +253,13 @@ const UserCallout = require('./user_callout');
case
'
projects:blob:show
'
:
case
'
projects:blob:show
'
:
case
'
projects:blame:show
'
:
case
'
projects:blame:show
'
:
new
LineHighlighter
();
new
LineHighlighter
();
new
BlobLinePermalinkUpdater
(
document
.
querySelector
(
'
#blob-content-holder
'
),
'
.diff-line-num[data-line-number]
'
,
document
.
querySelectorAll
(
'
.js-data-file-blob-permalink-url, .js-blob-blame-link
'
),
);
shortcut_handler
=
new
ShortcutsNavigation
();
shortcut_handler
=
new
ShortcutsNavigation
();
const
fileBlobPermalinkUrlElement
=
document
.
querySelector
(
'
.js-data-file-blob-permalink-url
'
);
const
fileBlobPermalinkUrlElement
=
document
.
querySelector
(
'
.js-data-file-blob-permalink-url
'
);
const
fileBlobPermalinkUrl
=
fileBlobPermalinkUrlElement
&&
fileBlobPermalinkUrlElement
.
getAttribute
(
'
href
'
);
const
fileBlobPermalinkUrl
=
fileBlobPermalinkUrlElement
&&
fileBlobPermalinkUrlElement
.
getAttribute
(
'
href
'
);
...
...
app/assets/javascripts/line_highlighter.js
View file @
c0242485
...
@@ -67,17 +67,7 @@ require('vendor/jquery.scrollTo');
...
@@ -67,17 +67,7 @@ require('vendor/jquery.scrollTo');
}
}
LineHighlighter
.
prototype
.
bindEvents
=
function
()
{
LineHighlighter
.
prototype
.
bindEvents
=
function
()
{
$
(
'
#blob-content-holder
'
).
on
(
'
mousedown
'
,
'
a[data-line-number]
'
,
this
.
clickHandler
);
$
(
'
#blob-content-holder
'
).
on
(
'
click
'
,
'
a[data-line-number]
'
,
this
.
clickHandler
);
// While it may seem odd to bind to the mousedown event and then throw away
// the click event, there is a method to our madness.
//
// If not done this way, the line number anchor will sometimes keep its
// active state even when the event is cancelled, resulting in an ugly border
// around the link and/or a persisted underline text decoration.
$
(
'
#blob-content-holder
'
).
on
(
'
click
'
,
'
a[data-line-number]
'
,
function
(
event
)
{
event
.
preventDefault
();
event
.
stopPropagation
();
});
};
};
LineHighlighter
.
prototype
.
clickHandler
=
function
(
event
)
{
LineHighlighter
.
prototype
.
clickHandler
=
function
(
event
)
{
...
...
app/assets/stylesheets/framework/highlight.scss
View file @
c0242485
...
@@ -57,9 +57,14 @@
...
@@ -57,9 +57,14 @@
visibility
:
hidden
;
visibility
:
hidden
;
}
}
&
:hover
i
{
&
:hover
,
&
:focus
{
outline
:
none
;
&
i
{
visibility
:
visible
;
visibility
:
visible
;
}
}
}
}
}
}
}
}
}
app/views/projects/blob/_actions.html.haml
View file @
c0242485
...
@@ -12,7 +12,7 @@
...
@@ -12,7 +12,7 @@
class:
'btn btn-sm'
class:
'btn btn-sm'
-
else
-
else
=
link_to
'Blame'
,
namespace_project_blame_path
(
@project
.
namespace
,
@project
,
@id
),
=
link_to
'Blame'
,
namespace_project_blame_path
(
@project
.
namespace
,
@project
,
@id
),
class:
'btn btn-sm'
unless
@blob
.
empty?
class:
'btn btn-sm
js-blob-blame-link
'
unless
@blob
.
empty?
=
link_to
'History'
,
namespace_project_commits_path
(
@project
.
namespace
,
@project
,
@id
),
=
link_to
'History'
,
namespace_project_commits_path
(
@project
.
namespace
,
@project
,
@id
),
class:
'btn btn-sm'
class:
'btn btn-sm'
=
link_to
'Permalink'
,
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
=
link_to
'Permalink'
,
namespace_project_blob_path
(
@project
.
namespace
,
@project
,
...
...
changelogs/unreleased/19742-permalink-blame-button-line-number-hash-links.yml
0 → 100644
View file @
c0242485
---
title
:
Update permalink/blame buttons with line number fragment hash
merge_request
:
author
:
spec/features/projects/blobs/blob_line_permalink_updater_spec.rb
0 → 100644
View file @
c0242485
require
'spec_helper'
feature
'Blob button line permalinks (BlobLinePermalinkUpdater)'
,
feature:
true
,
js:
true
do
include
TreeHelper
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
let
(
:path
)
{
'CHANGELOG'
}
let
(
:sha
)
{
project
.
repository
.
commit
.
sha
}
describe
'On a file(blob)'
do
def
get_absolute_url
(
path
=
""
)
"http://
#{
page
.
server
.
host
}
:
#{
page
.
server
.
port
}#{
path
}
"
end
def
visit_blob
(
fragment
=
nil
)
visit
namespace_project_blob_path
(
project
.
namespace
,
project
,
tree_join
(
'master'
,
path
),
anchor:
fragment
)
end
describe
'Click "Permalink" button'
do
it
'works with no initial line number fragment hash'
do
visit_blob
expect
(
find
(
'.js-data-file-blob-permalink-url'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blob_path
(
project
.
namespace
,
project
,
tree_join
(
sha
,
path
))))
end
it
'maintains intitial fragment hash'
do
fragment
=
"L3"
visit_blob
(
fragment
)
expect
(
find
(
'.js-data-file-blob-permalink-url'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blob_path
(
project
.
namespace
,
project
,
tree_join
(
sha
,
path
),
anchor:
fragment
)))
end
it
'changes fragment hash if line number clicked'
do
ending_fragment
=
"L5"
visit_blob
find
(
'#L3'
).
click
find
(
"#
#{
ending_fragment
}
"
).
click
expect
(
find
(
'.js-data-file-blob-permalink-url'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blob_path
(
project
.
namespace
,
project
,
tree_join
(
sha
,
path
),
anchor:
ending_fragment
)))
end
it
'with initial fragment hash, changes fragment hash if line number clicked'
do
fragment
=
"L1"
ending_fragment
=
"L5"
visit_blob
(
fragment
)
find
(
'#L3'
).
click
find
(
"#
#{
ending_fragment
}
"
).
click
expect
(
find
(
'.js-data-file-blob-permalink-url'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blob_path
(
project
.
namespace
,
project
,
tree_join
(
sha
,
path
),
anchor:
ending_fragment
)))
end
end
describe
'Click "Blame" button'
do
it
'works with no initial line number fragment hash'
do
visit_blob
expect
(
find
(
'.js-blob-blame-link'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blame_path
(
project
.
namespace
,
project
,
tree_join
(
'master'
,
path
))))
end
it
'maintains intitial fragment hash'
do
fragment
=
"L3"
visit_blob
(
fragment
)
expect
(
find
(
'.js-blob-blame-link'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blame_path
(
project
.
namespace
,
project
,
tree_join
(
'master'
,
path
),
anchor:
fragment
)))
end
it
'changes fragment hash if line number clicked'
do
ending_fragment
=
"L5"
visit_blob
find
(
'#L3'
).
click
find
(
"#
#{
ending_fragment
}
"
).
click
expect
(
find
(
'.js-blob-blame-link'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blame_path
(
project
.
namespace
,
project
,
tree_join
(
'master'
,
path
),
anchor:
ending_fragment
)))
end
it
'with initial fragment hash, changes fragment hash if line number clicked'
do
fragment
=
"L1"
ending_fragment
=
"L5"
visit_blob
(
fragment
)
find
(
'#L3'
).
click
find
(
"#
#{
ending_fragment
}
"
).
click
expect
(
find
(
'.js-blob-blame-link'
)[
'href'
]).
to
eq
(
get_absolute_url
(
namespace_project_blame_path
(
project
.
namespace
,
project
,
tree_join
(
'master'
,
path
),
anchor:
ending_fragment
)))
end
end
end
end
spec/javascripts/line_highlighter_spec.js
View file @
c0242485
...
@@ -7,16 +7,12 @@ require('~/line_highlighter');
...
@@ -7,16 +7,12 @@ require('~/line_highlighter');
describe
(
'
LineHighlighter
'
,
function
()
{
describe
(
'
LineHighlighter
'
,
function
()
{
var
clickLine
;
var
clickLine
;
preloadFixtures
(
'
static/line_highlighter.html.raw
'
);
preloadFixtures
(
'
static/line_highlighter.html.raw
'
);
clickLine
=
function
(
number
,
eventData
)
{
clickLine
=
function
(
number
,
eventData
=
{})
{
var
e
;
if
(
eventData
==
null
)
{
eventData
=
{};
}
if
(
$
.
isEmptyObject
(
eventData
))
{
if
(
$
.
isEmptyObject
(
eventData
))
{
return
$
(
"
#L
"
+
number
).
mousedown
().
click
();
return
$
(
"
#L
"
+
number
).
click
();
}
else
{
}
else
{
e
=
$
.
Event
(
'
mousedown
'
,
eventData
);
const
e
=
$
.
Event
(
'
click
'
,
eventData
);
return
$
(
"
#L
"
+
number
).
trigger
(
e
)
.
click
()
;
return
$
(
"
#L
"
+
number
).
trigger
(
e
);
}
}
};
};
beforeEach
(
function
()
{
beforeEach
(
function
()
{
...
@@ -63,12 +59,6 @@ require('~/line_highlighter');
...
@@ -63,12 +59,6 @@ require('~/line_highlighter');
});
});
});
});
describe
(
'
#clickHandler
'
,
function
()
{
describe
(
'
#clickHandler
'
,
function
()
{
it
(
'
discards the mousedown event
'
,
function
()
{
var
spy
;
spy
=
spyOnEvent
(
'
a[data-line-number]
'
,
'
mousedown
'
);
clickLine
(
13
);
return
expect
(
spy
).
toHaveBeenPrevented
();
});
it
(
'
handles clicking on a child icon element
'
,
function
()
{
it
(
'
handles clicking on a child icon element
'
,
function
()
{
var
spy
;
var
spy
;
spy
=
spyOn
(
this
[
"
class
"
],
'
setHash
'
).
and
.
callThrough
();
spy
=
spyOn
(
this
[
"
class
"
],
'
setHash
'
).
and
.
callThrough
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment