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
3581d619
Unverified
Commit
3581d619
authored
Dec 14, 2018
by
Daniel Lee
Committed by
GitHub
Dec 14, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #14499 from grafana/14483/copy-invite-link-fix
Fix for copy invite link
parents
82adf539
d37dae34
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
166 additions
and
288 deletions
+166
-288
public/app/features/users/InviteeRow.tsx
+57
-0
public/app/features/users/InviteesTable.test.tsx
+0
-1
public/app/features/users/InviteesTable.tsx
+4
-37
public/app/features/users/UsersListPage.test.tsx
+0
-1
public/app/features/users/UsersListPage.tsx
+2
-8
public/app/features/users/__mocks__/userMocks.ts
+1
-1
public/app/features/users/__snapshots__/InviteesTable.test.tsx.snap
+102
-240
No files found.
public/app/features/users/InviteeRow.tsx
0 → 100644
View file @
3581d619
import
React
,
{
createRef
,
PureComponent
}
from
'react'
;
import
{
connect
}
from
'react-redux'
;
import
{
Invitee
}
from
'app/types'
;
import
{
revokeInvite
}
from
'./state/actions'
;
export
interface
Props
{
invitee
:
Invitee
;
revokeInvite
:
typeof
revokeInvite
;
}
class
InviteeRow
extends
PureComponent
<
Props
>
{
private
copyUrlRef
=
createRef
<
HTMLTextAreaElement
>
();
copyToClipboard
=
()
=>
{
const
node
=
this
.
copyUrlRef
.
current
;
if
(
node
)
{
node
.
select
();
document
.
execCommand
(
'copy'
);
}
};
render
()
{
const
{
invitee
,
revokeInvite
}
=
this
.
props
;
return
(
<
tr
>
<
td
>
{
invitee
.
email
}
</
td
>
<
td
>
{
invitee
.
name
}
</
td
>
<
td
className=
"text-right"
>
<
button
className=
"btn btn-inverse btn-mini"
onClick=
{
this
.
copyToClipboard
}
>
<
textarea
readOnly=
{
true
}
value=
{
invitee
.
url
}
style=
{
{
position
:
'absolute'
,
right
:
-
1000
}
}
ref=
{
this
.
copyUrlRef
}
/>
<
i
className=
"fa fa-clipboard"
/>
Copy Invite
</
button
>
</
td
>
<
td
>
<
button
className=
"btn btn-danger btn-mini"
onClick=
{
()
=>
revokeInvite
(
invitee
.
code
)
}
>
<
i
className=
"fa fa-remove"
/>
</
button
>
</
td
>
</
tr
>
);
}
}
const
mapDispatchToProps
=
{
revokeInvite
,
};
export
default
connect
(()
=>
{
return
{};
},
mapDispatchToProps
)(
InviteeRow
);
public/app/features/users/InviteesTable.test.tsx
View file @
3581d619
...
@@ -7,7 +7,6 @@ import { getMockInvitees } from './__mocks__/userMocks';
...
@@ -7,7 +7,6 @@ import { getMockInvitees } from './__mocks__/userMocks';
const
setup
=
(
propOverrides
?:
object
)
=>
{
const
setup
=
(
propOverrides
?:
object
)
=>
{
const
props
:
Props
=
{
const
props
:
Props
=
{
invitees
:
[]
as
Invitee
[],
invitees
:
[]
as
Invitee
[],
onRevokeInvite
:
jest
.
fn
(),
};
};
Object
.
assign
(
props
,
propOverrides
);
Object
.
assign
(
props
,
propOverrides
);
...
...
public/app/features/users/InviteesTable.tsx
View file @
3581d619
import
React
,
{
createRef
,
PureComponent
}
from
'react'
;
import
React
,
{
PureComponent
}
from
'react'
;
import
{
Invitee
}
from
'app/types'
;
import
{
Invitee
}
from
'app/types'
;
import
InviteeRow
from
'./InviteeRow'
;
export
interface
Props
{
export
interface
Props
{
invitees
:
Invitee
[];
invitees
:
Invitee
[];
onRevokeInvite
:
(
code
:
string
)
=>
void
;
}
}
export
default
class
InviteesTable
extends
PureComponent
<
Props
>
{
export
default
class
InviteesTable
extends
PureComponent
<
Props
>
{
private
copyUrlRef
=
createRef
<
HTMLTextAreaElement
>
();
copyToClipboard
=
()
=>
{
const
node
=
this
.
copyUrlRef
.
current
;
if
(
node
)
{
node
.
select
();
document
.
execCommand
(
'copy'
);
}
};
render
()
{
render
()
{
const
{
invitees
,
onRevokeInvite
}
=
this
.
props
;
const
{
invitees
}
=
this
.
props
;
return
(
return
(
<
table
className=
"filter-table form-inline"
>
<
table
className=
"filter-table form-inline"
>
...
@@ -33,29 +22,7 @@ export default class InviteesTable extends PureComponent<Props> {
...
@@ -33,29 +22,7 @@ export default class InviteesTable extends PureComponent<Props> {
</
thead
>
</
thead
>
<
tbody
>
<
tbody
>
{
invitees
.
map
((
invitee
,
index
)
=>
{
{
invitees
.
map
((
invitee
,
index
)
=>
{
return
(
return
<
InviteeRow
key=
{
`${invitee.id}-${index}`
}
invitee=
{
invitee
}
/>;
<
tr
key=
{
`${invitee.id}-${index}`
}
>
<
td
>
{
invitee
.
email
}
</
td
>
<
td
>
{
invitee
.
name
}
</
td
>
<
td
className=
"text-right"
>
<
button
className=
"btn btn-inverse btn-mini"
onClick=
{
this
.
copyToClipboard
}
>
<
textarea
readOnly=
{
true
}
value=
{
invitee
.
url
}
style=
{
{
position
:
'absolute'
,
right
:
-
1000
}
}
ref=
{
this
.
copyUrlRef
}
/>
<
i
className=
"fa fa-clipboard"
/>
Copy Invite
</
button
>
</
td
>
<
td
>
<
button
className=
"btn btn-danger btn-mini"
onClick=
{
()
=>
onRevokeInvite
(
invitee
.
code
)
}
>
<
i
className=
"fa fa-remove"
/>
</
button
>
</
td
>
</
tr
>
);
})
}
})
}
</
tbody
>
</
tbody
>
</
table
>
</
table
>
...
...
public/app/features/users/UsersListPage.test.tsx
View file @
3581d619
...
@@ -16,7 +16,6 @@ const setup = (propOverrides?: object) => {
...
@@ -16,7 +16,6 @@ const setup = (propOverrides?: object) => {
invitees
:
[]
as
Invitee
[],
invitees
:
[]
as
Invitee
[],
searchQuery
:
''
,
searchQuery
:
''
,
externalUserMngInfo
:
''
,
externalUserMngInfo
:
''
,
revokeInvite
:
jest
.
fn
(),
loadInvitees
:
jest
.
fn
(),
loadInvitees
:
jest
.
fn
(),
loadUsers
:
jest
.
fn
(),
loadUsers
:
jest
.
fn
(),
updateUser
:
jest
.
fn
(),
updateUser
:
jest
.
fn
(),
...
...
public/app/features/users/UsersListPage.tsx
View file @
3581d619
...
@@ -9,7 +9,7 @@ import UsersTable from './UsersTable';
...
@@ -9,7 +9,7 @@ import UsersTable from './UsersTable';
import
InviteesTable
from
'./InviteesTable'
;
import
InviteesTable
from
'./InviteesTable'
;
import
{
Invitee
,
NavModel
,
OrgUser
}
from
'app/types'
;
import
{
Invitee
,
NavModel
,
OrgUser
}
from
'app/types'
;
import
appEvents
from
'app/core/app_events'
;
import
appEvents
from
'app/core/app_events'
;
import
{
loadUsers
,
loadInvitees
,
revokeInvite
,
setUsersSearchQuery
,
updateUser
,
removeUser
}
from
'./state/actions'
;
import
{
loadUsers
,
loadInvitees
,
setUsersSearchQuery
,
updateUser
,
removeUser
}
from
'./state/actions'
;
import
{
getNavModel
}
from
'../../core/selectors/navModel'
;
import
{
getNavModel
}
from
'../../core/selectors/navModel'
;
import
{
getInvitees
,
getUsers
,
getUsersSearchQuery
}
from
'./state/selectors'
;
import
{
getInvitees
,
getUsers
,
getUsersSearchQuery
}
from
'./state/selectors'
;
...
@@ -25,7 +25,6 @@ export interface Props {
...
@@ -25,7 +25,6 @@ export interface Props {
setUsersSearchQuery
:
typeof
setUsersSearchQuery
;
setUsersSearchQuery
:
typeof
setUsersSearchQuery
;
updateUser
:
typeof
updateUser
;
updateUser
:
typeof
updateUser
;
removeUser
:
typeof
removeUser
;
removeUser
:
typeof
removeUser
;
revokeInvite
:
typeof
revokeInvite
;
}
}
export
interface
State
{
export
interface
State
{
...
@@ -79,10 +78,6 @@ export class UsersListPage extends PureComponent<Props, State> {
...
@@ -79,10 +78,6 @@ export class UsersListPage extends PureComponent<Props, State> {
});
});
};
};
onRevokeInvite
=
code
=>
{
this
.
props
.
revokeInvite
(
code
);
};
onShowInvites
=
()
=>
{
onShowInvites
=
()
=>
{
this
.
setState
(
prevState
=>
({
this
.
setState
(
prevState
=>
({
showInvites
:
!
prevState
.
showInvites
,
showInvites
:
!
prevState
.
showInvites
,
...
@@ -93,7 +88,7 @@ export class UsersListPage extends PureComponent<Props, State> {
...
@@ -93,7 +88,7 @@ export class UsersListPage extends PureComponent<Props, State> {
const
{
invitees
,
users
}
=
this
.
props
;
const
{
invitees
,
users
}
=
this
.
props
;
if
(
this
.
state
.
showInvites
)
{
if
(
this
.
state
.
showInvites
)
{
return
<
InviteesTable
invitees=
{
invitees
}
onRevokeInvite=
{
code
=>
this
.
onRevokeInvite
(
code
)
}
/>;
return
<
InviteesTable
invitees=
{
invitees
}
/>;
}
else
{
}
else
{
return
(
return
(
<
UsersTable
<
UsersTable
...
@@ -141,7 +136,6 @@ const mapDispatchToProps = {
...
@@ -141,7 +136,6 @@ const mapDispatchToProps = {
setUsersSearchQuery
,
setUsersSearchQuery
,
updateUser
,
updateUser
,
removeUser
,
removeUser
,
revokeInvite
,
};
};
export
default
hot
(
module
)(
connect
(
mapStateToProps
,
mapDispatchToProps
)(
UsersListPage
));
export
default
hot
(
module
)(
connect
(
mapStateToProps
,
mapDispatchToProps
)(
UsersListPage
));
public/app/features/users/__mocks__/userMocks.ts
View file @
3581d619
...
@@ -48,7 +48,7 @@ export const getMockInvitees = (amount: number) => {
...
@@ -48,7 +48,7 @@ export const getMockInvitees = (amount: number) => {
orgId
:
1
,
orgId
:
1
,
role
:
'viewer'
,
role
:
'viewer'
,
status
:
'not accepted'
,
status
:
'not accepted'
,
url
:
`localhost/invite/$
$
{
i
}
`
,
url
:
`localhost/invite/
${
i
}
`
,
});
});
}
}
...
...
public/app/features/users/__snapshots__/InviteesTable.test.tsx.snap
View file @
3581d619
...
@@ -49,270 +49,132 @@ exports[`Render should render invitees 1`] = `
...
@@ -49,270 +49,132 @@ exports[`Render should render invitees 1`] = `
</tr>
</tr>
</thead>
</thead>
<tbody>
<tbody>
<tr
<Connect(InviteeRow)
key="0-0"
invitee={
>
<td>
invitee-0@test.com
</td>
<td>
invitee-0
</td>
<td
className="text-right"
>
<button
className="btn btn-inverse btn-mini"
onClick={[Function]}
>
<textarea
readOnly={true}
style={
Object {
Object {
"position": "absolute",
"code": "asdfasdfsadf-0",
"right": -1000,
"createdOn": "2018-10-02",
"email": "invitee-0@test.com",
"emailSent": true,
"emailSentOn": "2018-10-02",
"id": 0,
"invitedByEmail": "admin@grafana.com",
"invitedByLogin": "admin",
"invitedByName": "admin",
"name": "invitee-0",
"orgId": 1,
"role": "viewer",
"status": "not accepted",
"url": "localhost/invite/0",
}
}
}
}
value="localhost/invite/$0"
key="0-0"
/>
<i
className="fa fa-clipboard"
/>
Copy Invite
</button>
</td>
<td>
<button
className="btn btn-danger btn-mini"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
/>
</button>
<Connect(InviteeRow)
</td>
invitee={
</tr>
<tr
key="1-1"
>
<td>
invitee-1@test.com
</td>
<td>
invitee-1
</td>
<td
className="text-right"
>
<button
className="btn btn-inverse btn-mini"
onClick={[Function]}
>
<textarea
readOnly={true}
style={
Object {
Object {
"position": "absolute",
"code": "asdfasdfsadf-1",
"right": -1000,
"createdOn": "2018-10-02",
"email": "invitee-1@test.com",
"emailSent": true,
"emailSentOn": "2018-10-02",
"id": 1,
"invitedByEmail": "admin@grafana.com",
"invitedByLogin": "admin",
"invitedByName": "admin",
"name": "invitee-1",
"orgId": 1,
"role": "viewer",
"status": "not accepted",
"url": "localhost/invite/1",
}
}
}
}
value="localhost/invite/$1"
key="1-1"
/>
<i
className="fa fa-clipboard"
/>
Copy Invite
</button>
</td>
<td>
<button
className="btn btn-danger btn-mini"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
/>
</button>
<Connect(InviteeRow)
</td>
invitee={
</tr>
<tr
key="2-2"
>
<td>
invitee-2@test.com
</td>
<td>
invitee-2
</td>
<td
className="text-right"
>
<button
className="btn btn-inverse btn-mini"
onClick={[Function]}
>
<textarea
readOnly={true}
style={
Object {
Object {
"position": "absolute",
"code": "asdfasdfsadf-2",
"right": -1000,
"createdOn": "2018-10-02",
"email": "invitee-2@test.com",
"emailSent": true,
"emailSentOn": "2018-10-02",
"id": 2,
"invitedByEmail": "admin@grafana.com",
"invitedByLogin": "admin",
"invitedByName": "admin",
"name": "invitee-2",
"orgId": 1,
"role": "viewer",
"status": "not accepted",
"url": "localhost/invite/2",
}
}
}
}
value="localhost/invite/$2"
key="2-2"
/>
<i
className="fa fa-clipboard"
/>
Copy Invite
</button>
</td>
<td>
<button
className="btn btn-danger btn-mini"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
/>
</button>
<Connect(InviteeRow)
</td>
invitee={
</tr>
<tr
key="3-3"
>
<td>
invitee-3@test.com
</td>
<td>
invitee-3
</td>
<td
className="text-right"
>
<button
className="btn btn-inverse btn-mini"
onClick={[Function]}
>
<textarea
readOnly={true}
style={
Object {
Object {
"position": "absolute",
"code": "asdfasdfsadf-3",
"right": -1000,
"createdOn": "2018-10-02",
"email": "invitee-3@test.com",
"emailSent": true,
"emailSentOn": "2018-10-02",
"id": 3,
"invitedByEmail": "admin@grafana.com",
"invitedByLogin": "admin",
"invitedByName": "admin",
"name": "invitee-3",
"orgId": 1,
"role": "viewer",
"status": "not accepted",
"url": "localhost/invite/3",
}
}
}
}
value="localhost/invite/$3"
key="3-3"
/>
<i
className="fa fa-clipboard"
/>
Copy Invite
</button>
</td>
<td>
<button
className="btn btn-danger btn-mini"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
/>
</button>
<Connect(InviteeRow)
</td>
invitee={
</tr>
<tr
key="4-4"
>
<td>
invitee-4@test.com
</td>
<td>
invitee-4
</td>
<td
className="text-right"
>
<button
className="btn btn-inverse btn-mini"
onClick={[Function]}
>
<textarea
readOnly={true}
style={
Object {
Object {
"position": "absolute",
"code": "asdfasdfsadf-4",
"right": -1000,
"createdOn": "2018-10-02",
"email": "invitee-4@test.com",
"emailSent": true,
"emailSentOn": "2018-10-02",
"id": 4,
"invitedByEmail": "admin@grafana.com",
"invitedByLogin": "admin",
"invitedByName": "admin",
"name": "invitee-4",
"orgId": 1,
"role": "viewer",
"status": "not accepted",
"url": "localhost/invite/4",
}
}
}
}
value="localhost/invite/$4"
key="4-4"
/>
<i
className="fa fa-clipboard"
/>
Copy Invite
</button>
</td>
<td>
<button
className="btn btn-danger btn-mini"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
/>
</button>
<Connect(InviteeRow)
</td>
invitee={
</tr>
<tr
key="5-5"
>
<td>
invitee-5@test.com
</td>
<td>
invitee-5
</td>
<td
className="text-right"
>
<button
className="btn btn-inverse btn-mini"
onClick={[Function]}
>
<textarea
readOnly={true}
style={
Object {
Object {
"position": "absolute",
"code": "asdfasdfsadf-5",
"right": -1000,
"createdOn": "2018-10-02",
"email": "invitee-5@test.com",
"emailSent": true,
"emailSentOn": "2018-10-02",
"id": 5,
"invitedByEmail": "admin@grafana.com",
"invitedByLogin": "admin",
"invitedByName": "admin",
"name": "invitee-5",
"orgId": 1,
"role": "viewer",
"status": "not accepted",
"url": "localhost/invite/5",
}
}
}
}
value="localhost/invite/$5"
key="5-5"
/>
<i
className="fa fa-clipboard"
/>
Copy Invite
</button>
</td>
<td>
<button
className="btn btn-danger btn-mini"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
/>
</button>
</td>
</tr>
</tbody>
</tbody>
</table>
</table>
`;
`;
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