Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
bc81298d
Commit
bc81298d
authored
Nov 24, 2017
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
worked on search
parent
93d21bec
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
245 additions
and
78 deletions
+245
-78
pkg/services/search/models.go
+1
-0
public/app/core/components/search/search.ts
+1
-1
public/app/core/services/impression_srv.ts
+3
-6
public/app/core/services/search_srv.ts
+104
-22
public/app/core/specs/search_srv.jest.ts
+123
-22
public/app/features/dashboard/all.ts
+0
-2
public/app/features/dashboard/dashboardLoaderSrv.js
+4
-3
public/app/features/dashboard/dashboard_list_ctrl.ts
+1
-9
public/app/features/dashboard/specs/dashboard_list_ctrl.jest.ts
+3
-6
public/app/features/plugins/plugin_loader.ts
+2
-2
public/app/plugins/panel/dashlist/module.ts
+2
-2
public/app/plugins/panel/singlestat/module.ts
+0
-2
public/sass/layout/_page.scss
+1
-1
No files found.
pkg/services/search/models.go
View file @
bc81298d
...
...
@@ -63,6 +63,7 @@ type FindPersistedDashboardsQuery struct {
FolderIds
[]
int64
Tags
[]
string
Limit
int
IsBrowse
bool
Result
HitList
}
public/app/core/components/search/search.ts
View file @
bc81298d
...
...
@@ -151,7 +151,7 @@ export class SearchCtrl {
}
toggleFolder
(
section
)
{
this
.
searchSrv
.
toggle
Folder
(
section
);
this
.
searchSrv
.
toggle
Section
(
section
);
}
}
...
...
public/app/
features/dashboard/impression_store
.ts
→
public/app/
core/services/impression_srv
.ts
View file @
bc81298d
...
...
@@ -2,7 +2,7 @@ import store from 'app/core/store';
import
_
from
'lodash'
;
import
config
from
'app/core/config'
;
export
class
Impression
sStore
{
export
class
Impression
Srv
{
constructor
()
{}
addDashboardImpression
(
dashboardId
)
{
...
...
@@ -44,8 +44,5 @@ export class ImpressionsStore {
}
}
var
impressions
=
new
ImpressionsStore
();
export
{
impressions
};
const
impressionSrv
=
new
ImpressionSrv
();
export
default
impressionSrv
;
public/app/core/services/search_srv.ts
View file @
bc81298d
import
_
from
'lodash'
;
import
coreModule
from
'app/core/core_module'
;
import
impressionSrv
from
'app/core/services/impression_srv'
;
import
store
from
'app/core/store'
;
export
class
SearchSrv
{
recentIsOpen
:
boolean
;
starredIsOpen
:
boolean
;
/** @ngInject */
constructor
(
private
backendSrv
)
{
constructor
(
private
backendSrv
,
private
$q
)
{
this
.
recentIsOpen
=
store
.
getBool
(
'search.sections.recent'
,
true
);
this
.
starredIsOpen
=
store
.
getBool
(
'search.sections.starred'
,
true
);
}
browse
()
{
private
getRecentDashboards
(
sections
)
{
return
this
.
queryForRecentDashboards
().
then
(
result
=>
{
if
(
result
.
length
>
0
)
{
sections
[
'recent'
]
=
{
title
:
'Recent Boards'
,
icon
:
'fa fa-clock-o'
,
score
:
-
1
,
expanded
:
this
.
recentIsOpen
,
toggle
:
this
.
toggleRecent
.
bind
(
this
),
items
:
result
,
};
}
});
}
private
queryForRecentDashboards
()
{
var
dashIds
=
_
.
take
(
impressionSrv
.
getDashboardOpened
(),
5
);
if
(
dashIds
.
length
===
0
)
{
return
Promise
.
resolve
([]);
}
return
this
.
backendSrv
.
search
({
dashboardIds
:
dashIds
}).
then
(
result
=>
{
return
dashIds
.
map
(
orderId
=>
{
return
this
.
transformToViewModel
(
_
.
find
(
result
,
{
id
:
orderId
}));
}).
filter
(
item
=>
!
item
.
isStarred
);
});
}
private
toggleRecent
(
section
)
{
this
.
recentIsOpen
=
section
.
expanded
=
!
section
.
expanded
;
store
.
set
(
'search.sections.recent'
,
this
.
recentIsOpen
);
if
(
!
section
.
expanded
||
section
.
items
.
length
)
{
return
;
}
return
this
.
queryForRecentDashboards
().
then
(
result
=>
{
section
.
items
=
result
;
});
}
private
toggleStarred
(
section
)
{
this
.
starredIsOpen
=
section
.
expanded
=
!
section
.
expanded
;
store
.
set
(
'search.sections.starred'
,
this
.
starredIsOpen
);
}
private
getStarred
(
sections
)
{
return
this
.
backendSrv
.
search
({
starred
:
true
,
limit
:
5
}).
then
(
result
=>
{
if
(
result
.
length
>
0
)
{
sections
[
'starred'
]
=
{
title
:
'Starred Boards'
,
icon
:
'fa fa-star-o'
,
score
:
-
2
,
expanded
:
this
.
starredIsOpen
,
toggle
:
this
.
toggleStarred
.
bind
(
this
),
items
:
this
.
transformToViewModel
(
result
),
};
}
});
}
private
getDashboardsAndFolders
(
sections
)
{
const
rootFolderId
=
0
;
let
query
=
{
folderIds
:
[
rootFolderId
]
folderIds
:
[
rootFolderId
]
,
};
return
this
.
backendSrv
.
search
(
query
).
then
(
results
=>
{
let
sections
:
any
=
{};
for
(
let
hit
of
results
)
{
if
(
hit
.
type
===
'dash-folder'
)
{
sections
[
hit
.
id
]
=
{
...
...
@@ -26,7 +90,8 @@ export class SearchSrv {
items
:
[],
icon
:
'fa fa-folder'
,
score
:
_
.
keys
(
sections
).
length
,
uri
:
hit
.
uri
uri
:
hit
.
uri
,
toggle
:
this
.
toggleFolder
.
bind
(
this
),
};
}
}
...
...
@@ -37,7 +102,7 @@ export class SearchSrv {
items
:
[],
icon
:
'fa fa-folder-open'
,
score
:
_
.
keys
(
sections
).
length
,
expanded
:
true
expanded
:
true
,
};
for
(
let
hit
of
results
)
{
...
...
@@ -45,18 +110,36 @@ export class SearchSrv {
continue
;
}
let
section
=
sections
[
hit
.
folderId
||
0
];
hit
.
url
=
'dashboard/'
+
hit
.
uri
;
section
.
items
.
push
(
hit
);
if
(
section
)
{
section
.
items
.
push
(
this
.
transformToViewModel
(
hit
));
}
else
{
console
.
log
(
'Error: dashboard returned from browse search but not folder'
,
hit
.
id
,
hit
.
folderId
);
}
}
});
}
private
browse
()
{
let
sections
:
any
=
{};
let
promises
=
[
this
.
getRecentDashboards
(
sections
),
this
.
getStarred
(
sections
),
this
.
getDashboardsAndFolders
(
sections
),
];
return
this
.
$q
.
all
(
promises
).
then
(()
=>
{
return
_
.
sortBy
(
_
.
values
(
sections
),
'score'
);
});
}
private
transformToViewModel
(
hit
)
{
hit
.
url
=
'dashboard/'
+
hit
.
uri
;
return
hit
;
}
search
(
options
)
{
if
(
!
options
.
query
&&
(
!
options
.
tag
||
options
.
tag
.
length
===
0
)
&&
!
options
.
starred
)
{
if
(
!
options
.
query
&&
(
!
options
.
tag
||
options
.
tag
.
length
===
0
)
&&
!
options
.
starred
)
{
return
this
.
browse
();
}
...
...
@@ -65,7 +148,6 @@ export class SearchSrv {
query
.
type
=
'dash-db'
;
return
this
.
backendSrv
.
search
(
query
).
then
(
results
=>
{
let
section
=
{
hideHeader
:
true
,
items
:
[],
...
...
@@ -76,15 +158,14 @@ export class SearchSrv {
if
(
hit
.
type
===
'dash-folder'
)
{
continue
;
}
hit
.
url
=
'dashboard/'
+
hit
.
uri
;
section
.
items
.
push
(
hit
);
section
.
items
.
push
(
this
.
transformToViewModel
(
hit
));
}
return
[
section
];
});
}
toggleFolder
(
section
)
{
private
toggleFolder
(
section
)
{
section
.
expanded
=
!
section
.
expanded
;
section
.
icon
=
section
.
expanded
?
'fa fa-folder-open'
:
'fa fa-folder'
;
...
...
@@ -93,17 +174,18 @@ export class SearchSrv {
}
let
query
=
{
folderIds
:
[
section
.
id
]
folderIds
:
[
section
.
id
]
,
};
return
this
.
backendSrv
.
search
(
query
).
then
(
results
=>
{
for
(
let
hit
of
results
)
{
hit
.
url
=
'dashboard/'
+
hit
.
uri
;
section
.
items
.
push
(
hit
);
}
section
.
items
=
_
.
map
(
results
,
this
.
transformToViewModel
);
});
}
toggleSection
(
section
)
{
section
.
toggle
(
section
);
}
getDashboardTags
()
{
return
this
.
backendSrv
.
get
(
'/api/dashboards/tags'
);
}
...
...
public/app/core/specs/search_srv.jest.ts
View file @
bc81298d
import
{
SearchSrv
}
from
'app/core/services/search_srv'
;
import
{
BackendSrvMock
}
from
'test/mocks/backend_srv'
;
import
impressionSrv
from
'app/core/services/impression_srv'
;
jest
.
mock
(
'app/core/store'
,
()
=>
{
return
{
getBool
:
jest
.
fn
(),
set
:
jest
.
fn
(),
};
});
jest
.
mock
(
'app/core/services/impression_srv'
,
()
=>
{
return
{
getDashboardOpened
:
jest
.
fn
,
};
});
describe
(
'SearchSrv'
,
()
=>
{
let
searchSrv
,
backendSrvMock
;
beforeEach
(()
=>
{
backendSrvMock
=
new
BackendSrvMock
();
searchSrv
=
new
SearchSrv
(
backendSrvMock
);
searchSrv
=
new
SearchSrv
(
backendSrvMock
,
Promise
);
impressionSrv
.
getDashboardOpened
=
jest
.
fn
().
mockReturnValue
([]);
});
describe
(
"with no query string and dashboards with folders returned"
,
()
=>
{
describe
(
'With recent dashboards'
,
()
=>
{
let
results
;
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
().
mockReturnValue
(
Promise
.
resolve
([
backendSrvMock
.
search
=
jest
.
fn
()
.
mockReturnValueOnce
(
Promise
.
resolve
([{
id
:
2
,
title
:
'second but first'
},
{
id
:
1
,
title
:
'first but second'
}]),
)
.
mockReturnValue
(
Promise
.
resolve
([]));
impressionSrv
.
getDashboardOpened
=
jest
.
fn
().
mockReturnValue
([
1
,
2
]);
return
searchSrv
.
search
({
query
:
''
}).
then
(
res
=>
{
results
=
res
;
});
});
it
(
'should include recent dashboards section'
,
()
=>
{
expect
(
results
[
0
].
title
).
toBe
(
'Recent Boards'
);
});
it
(
'should return order decided by impressions store not api'
,
()
=>
{
expect
(
results
[
0
].
items
[
0
].
title
).
toBe
(
'first but second'
);
expect
(
results
[
0
].
items
[
1
].
title
).
toBe
(
'second but first'
);
});
});
describe
(
'With starred dashboards'
,
()
=>
{
let
results
;
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
()
.
mockReturnValue
(
Promise
.
resolve
([
{
id
:
1
,
title
:
'starred'
}
]));
return
searchSrv
.
search
({
query
:
''
}).
then
(
res
=>
{
results
=
res
;
});
});
it
(
'should include starred dashboards section'
,
()
=>
{
expect
(
results
[
0
].
title
).
toBe
(
'Starred Boards'
);
expect
(
results
[
0
].
items
.
length
).
toBe
(
1
);
});
});
describe
(
'With starred dashboards and recent'
,
()
=>
{
let
results
;
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
()
.
mockReturnValueOnce
(
Promise
.
resolve
([
{
id
:
1
,
title
:
'starred and recent'
,
isStarred
:
true
},
{
id
:
2
,
title
:
'recent'
}
]))
.
mockReturnValue
(
Promise
.
resolve
([
{
id
:
1
,
title
:
'starred and recent'
}
]));
impressionSrv
.
getDashboardOpened
=
jest
.
fn
().
mockReturnValue
([
1
,
2
]);
return
searchSrv
.
search
({
query
:
''
}).
then
(
res
=>
{
results
=
res
;
});
});
it
(
'should not show starred in recent'
,
()
=>
{
expect
(
results
[
1
].
title
).
toBe
(
'Recent Boards'
);
expect
(
results
[
1
].
items
[
0
].
title
).
toBe
(
'recent'
);
});
it
(
'should show starred'
,
()
=>
{
expect
(
results
[
0
].
title
).
toBe
(
'Starred Boards'
);
expect
(
results
[
0
].
items
[
0
].
title
).
toBe
(
'starred and recent'
);
});
});
describe
(
'with no query string and dashboards with folders returned'
,
()
=>
{
let
results
;
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
()
.
mockReturnValueOnce
(
Promise
.
resolve
([]))
.
mockReturnValue
(
Promise
.
resolve
([
{
title
:
'folder1'
,
type
:
'dash-folder'
,
...
...
@@ -28,38 +129,39 @@ describe('SearchSrv', () => {
title
:
'dash in folder1 1'
,
type
:
'dash-db'
,
id
:
3
,
folderId
:
1
folderId
:
1
,
},
{
title
:
'dash in folder1 2'
,
type
:
'dash-db'
,
id
:
4
,
folderId
:
1
folderId
:
1
,
},
]));
]),
);
return
searchSrv
.
search
({
query
:
''
}).
then
(
res
=>
{
return
searchSrv
.
search
({
query
:
''
}).
then
(
res
=>
{
results
=
res
;
});
});
it
(
"should create sections for each folder and root"
,
()
=>
{
it
(
'should create sections for each folder and root'
,
()
=>
{
expect
(
results
).
toHaveLength
(
2
);
});
it
(
'should place folders first'
,
()
=>
{
expect
(
results
[
0
].
title
).
toBe
(
'folder1'
);
});
});
describe
(
"with query string and dashboards with folders returned"
,
()
=>
{
describe
(
'with query string and dashboards with folders returned'
,
()
=>
{
let
results
;
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
();
backendSrvMock
.
search
.
mockReturnValue
(
Promise
.
resolve
([
backendSrvMock
.
search
.
mockReturnValue
(
Promise
.
resolve
([
{
id
:
2
,
title
:
'dash with no folder'
,
...
...
@@ -72,14 +174,15 @@ describe('SearchSrv', () => {
folderId
:
1
,
folderTitle
:
'folder1'
,
},
]));
]),
);
return
searchSrv
.
search
({
query
:
'search'
}).
then
(
res
=>
{
return
searchSrv
.
search
({
query
:
'search'
}).
then
(
res
=>
{
results
=
res
;
});
});
it
(
"should not specify folder ids"
,
()
=>
{
it
(
'should not specify folder ids'
,
()
=>
{
expect
(
backendSrvMock
.
search
.
mock
.
calls
[
0
][
0
].
folderIds
).
toHaveLength
(
0
);
});
...
...
@@ -87,33 +190,31 @@ describe('SearchSrv', () => {
expect
(
results
).
toHaveLength
(
1
);
expect
(
results
[
0
].
hideHeader
).
toBe
(
true
);
});
});
describe
(
"with tags"
,
()
=>
{
describe
(
'with tags'
,
()
=>
{
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
();
backendSrvMock
.
search
.
mockReturnValue
(
Promise
.
resolve
([]));
return
searchSrv
.
search
({
tag
:
[
'atag'
]
}).
then
(()
=>
{});
return
searchSrv
.
search
({
tag
:
[
'atag'
]
}).
then
(()
=>
{});
});
it
(
"should send tags query to backend search"
,
()
=>
{
it
(
'should send tags query to backend search'
,
()
=>
{
expect
(
backendSrvMock
.
search
.
mock
.
calls
[
0
][
0
].
tag
).
toHaveLength
(
1
);
});
});
describe
(
"with starred"
,
()
=>
{
describe
(
'with starred'
,
()
=>
{
beforeEach
(()
=>
{
backendSrvMock
.
search
=
jest
.
fn
();
backendSrvMock
.
search
.
mockReturnValue
(
Promise
.
resolve
([]));
return
searchSrv
.
search
({
starred
:
true
}).
then
(()
=>
{});
return
searchSrv
.
search
({
starred
:
true
}).
then
(()
=>
{});
});
it
(
"should send starred query to backend search"
,
()
=>
{
it
(
'should send starred query to backend search'
,
()
=>
{
expect
(
backendSrvMock
.
search
.
mock
.
calls
[
0
][
0
].
starred
).
toEqual
(
true
);
});
});
});
public/app/features/dashboard/all.ts
View file @
bc81298d
import
'./dashboard_ctrl'
;
import
'./alerting_srv'
;
import
'./history/history'
;
...
...
@@ -15,7 +14,6 @@ import './time_srv';
import
'./unsavedChangesSrv'
;
import
'./unsaved_changes_modal'
;
import
'./timepicker/timepicker'
;
import
'./impression_store'
;
import
'./upload'
;
import
'./import/dash_import'
;
import
'./export/export_modal'
;
...
...
public/app/features/dashboard/dashboardLoaderSrv.js
View file @
bc81298d
...
...
@@ -5,12 +5,13 @@ define([
'jquery'
,
'app/core/utils/kbn'
,
'app/core/utils/datemath'
,
'
./impression_store
'
'
app/core/services/impression_srv
'
],
function
(
angular
,
moment
,
_
,
$
,
kbn
,
dateMath
,
impressionS
tore
)
{
function
(
angular
,
moment
,
_
,
$
,
kbn
,
dateMath
,
impressionS
rv
)
{
'use strict'
;
kbn
=
kbn
.
default
;
impressionSrv
=
impressionSrv
.
default
;
var
module
=
angular
.
module
(
'grafana.services'
);
...
...
@@ -50,7 +51,7 @@ function (angular, moment, _, $, kbn, dateMath, impressionStore) {
promise
.
then
(
function
(
result
)
{
if
(
result
.
meta
.
dashboardNotFound
!==
true
)
{
impressionS
tore
.
impressions
.
addDashboardImpression
(
result
.
dashboard
.
id
);
impressionS
rv
.
addDashboardImpression
(
result
.
dashboard
.
id
);
}
return
result
;
...
...
public/app/features/dashboard/dashboard_list_ctrl.ts
View file @
bc81298d
...
...
@@ -26,14 +26,6 @@ export class DashboardListCtrl {
}
getDashboards
()
{
if
(
this
.
query
.
query
.
length
===
0
&&
this
.
query
.
tag
.
length
===
0
&&
!
this
.
query
.
starred
)
{
return
this
.
searchSrv
.
browse
().
then
((
result
)
=>
{
return
this
.
initDashboardList
(
result
);
});
}
return
this
.
searchSrv
.
search
(
this
.
query
).
then
((
result
)
=>
{
return
this
.
initDashboardList
(
result
);
});
...
...
@@ -144,7 +136,7 @@ export class DashboardListCtrl {
}
toggleFolder
(
section
)
{
return
this
.
searchSrv
.
toggle
Folder
(
section
);
return
this
.
searchSrv
.
toggle
Section
(
section
);
}
getTags
()
{
...
...
public/app/features/dashboard/specs/dashboard_list_ctrl.jest.ts
View file @
bc81298d
...
...
@@ -93,7 +93,7 @@ describe('DashboardListCtrl', () => {
}
];
ctrl
=
createCtrlWithStubs
(
[],
response
);
ctrl
=
createCtrlWithStubs
(
response
);
});
describe
(
'with query filter'
,
()
=>
{
...
...
@@ -490,15 +490,12 @@ describe('DashboardListCtrl', () => {
});
});
function
createCtrlWithStubs
(
browseResponse
:
any
,
searchResponse
?
:
any
,
tags
?:
any
)
{
function
createCtrlWithStubs
(
searchResponse
:
any
,
tags
?:
any
)
{
const
searchSrvStub
=
{
browse
:
()
=>
{
return
q
.
resolve
(
browseResponse
);
},
search
:
(
options
:
any
)
=>
{
return
q
.
resolve
(
searchResponse
);
},
toggle
Folder
:
(
section
)
=>
{
toggle
Section
:
(
section
)
=>
{
return
;
},
getDashboardTags
:
()
=>
{
...
...
public/app/features/plugins/plugin_loader.ts
View file @
bc81298d
...
...
@@ -13,7 +13,7 @@ import * as datemath from 'app/core/utils/datemath';
import
*
as
fileExport
from
'app/core/utils/file_export'
;
import
*
as
flatten
from
'app/core/utils/flatten'
;
import
*
as
ticks
from
'app/core/utils/ticks'
;
import
{
impressions
}
from
'app/features/dashboard/impression_store
'
;
import
impressionSrv
from
'app/core/services/impression_srv
'
;
import
builtInPlugins
from
'./built_in_plugins'
;
import
*
as
d3
from
'd3'
;
...
...
@@ -78,7 +78,7 @@ exposeToPlugin('vendor/npm/rxjs/Rx', {
});
exposeToPlugin
(
'app/features/dashboard/impression_store'
,
{
impressions
:
impression
s
,
impressions
:
impression
Srv
,
__esModule
:
true
});
...
...
public/app/plugins/panel/dashlist/module.ts
View file @
bc81298d
import
_
from
'lodash'
;
import
{
PanelCtrl
}
from
'app/plugins/sdk'
;
import
{
impressions
}
from
'app/features/dashboard/impression_store
'
;
import
impressionSrv
from
'app/core/services/impression_srv
'
;
class
DashListCtrl
extends
PanelCtrl
{
static
templateUrl
=
'module.html'
;
...
...
@@ -123,7 +123,7 @@ class DashListCtrl extends PanelCtrl {
return
Promise
.
resolve
();
}
var
dashIds
=
_
.
take
(
impression
s
.
getDashboardOpened
(),
this
.
panel
.
limit
);
var
dashIds
=
_
.
take
(
impression
Srv
.
getDashboardOpened
(),
this
.
panel
.
limit
);
return
this
.
backendSrv
.
search
({
dashboardIds
:
dashIds
,
limit
:
this
.
panel
.
limit
}).
then
(
result
=>
{
this
.
groups
[
1
].
list
=
dashIds
.
map
(
orderId
=>
{
return
_
.
find
(
result
,
dashboard
=>
{
...
...
public/app/plugins/panel/singlestat/module.ts
View file @
bc81298d
...
...
@@ -592,8 +592,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
if
(
!
ctrl
.
data
)
{
return
;
}
data
=
ctrl
.
data
;
console
.
log
(
'singlestat'
,
elem
.
html
());
// get thresholds
data
.
thresholds
=
panel
.
thresholds
.
split
(
','
).
map
(
function
(
strVale
)
{
return
Number
(
strVale
.
trim
());
...
...
public/sass/layout/_page.scss
View file @
bc81298d
...
...
@@ -123,7 +123,7 @@
.page-breadcrumbs
{
display
:
flex
;
padding
:
3px
1
.5rem
1
.5rem
1
.5rem
;
padding
:
10px
25px
;
line-height
:
0
.5
;
}
...
...
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