Skip to content

Commit 75be787

Browse files
feat(multi-org): add user profile icon and popover menu (#5030)
1 parent c959253 commit 75be787

File tree

3 files changed

+191
-0
lines changed

3 files changed

+191
-0
lines changed

src/identity/components/GlobalHeader/GlobalHeader.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import {
2828
emptyOrg,
2929
} from 'src/identity/components/GlobalHeader/DefaultEntities'
3030
import {alphaSortSelectedFirst} from 'src/identity/utils/alphaSortSelectedFirst'
31+
import IdentityUserAvatar from 'src/identity/components/GlobalHeader/IdentityUserAvatar'
3132

3233
export const GlobalHeader: FC = () => {
3334
const dispatch = useDispatch()
@@ -88,6 +89,7 @@ export const GlobalHeader: FC = () => {
8889
</>
8990
)}
9091
</FlexBox>
92+
<IdentityUserAvatar user={identity.currentIdentity.user} />
9193
</FlexBox>
9294
)
9395
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import React from 'react'
2+
import {IdentityUser} from 'src/client/unityRoutes'
3+
import classnames from 'classnames'
4+
import {
5+
Button,
6+
ButtonShape,
7+
ClickOutside,
8+
ComponentColor,
9+
FlexBox,
10+
FlexDirection,
11+
Icon,
12+
IconFont,
13+
} from '@influxdata/clockface'
14+
15+
import './UserPopoverStyles.scss'
16+
import {Link} from 'react-router-dom'
17+
18+
type Props = {
19+
user: IdentityUser
20+
}
21+
22+
type State = {
23+
isPopoverOpen: boolean
24+
}
25+
26+
class IdentityUserAvatar extends React.Component<Props, State> {
27+
constructor(props: Props) {
28+
super(props)
29+
this.state = {
30+
isPopoverOpen: false,
31+
}
32+
}
33+
34+
private getInitials = (): string => {
35+
const {user} = this.props
36+
const firstName = user.firstName
37+
const lastName = user.lastName
38+
const initials = `${firstName.charAt(0)}${lastName.charAt(0)}`
39+
return initials
40+
}
41+
42+
private togglePopoverState = () => {
43+
const {isPopoverOpen} = this.state
44+
this.setState({isPopoverOpen: !isPopoverOpen})
45+
}
46+
47+
private setPopoverStateClosed = () => {
48+
this.setState({isPopoverOpen: false})
49+
}
50+
51+
private getUserPopoverContents = () => {
52+
const {user} = this.props
53+
return (
54+
<>
55+
<div className="user-popover-header">
56+
<div className="user-popover-header-name">
57+
{user.firstName} {user.lastName}
58+
</div>
59+
<div className="user-popover-header-email">{user.email}</div>
60+
<hr />
61+
</div>
62+
<div className="user-popover-footer">
63+
<Link className="user-popover-footer--button" to="/">
64+
<Icon
65+
glyph={IconFont.UserOutline_New}
66+
className="user-popover-footer--button-icon"
67+
/>
68+
Profile
69+
</Link>
70+
<Link className="user-popover-footer--button" to="/logout">
71+
<Icon
72+
glyph={IconFont.Logout_New}
73+
className="user-popover-footer--button-icon"
74+
/>
75+
Log Out
76+
</Link>
77+
</div>
78+
</>
79+
)
80+
}
81+
82+
render() {
83+
const userPopoverClassName = classnames('user-popover', {
84+
'user-popover--open': this.state.isPopoverOpen,
85+
})
86+
87+
const userAvatarButtonClassName = classnames('user-avatar-button', {
88+
'user-popover--open': this.state.isPopoverOpen,
89+
})
90+
91+
const {isPopoverOpen} = this.state
92+
return (
93+
<ClickOutside onClickOutside={this.setPopoverStateClosed}>
94+
<div>
95+
{/* Button shape is ButtonShape.Square to make the height and width the same
96+
so we can use the border radius to make it a circle */}
97+
<Button
98+
text={this.getInitials()}
99+
shape={ButtonShape.Square}
100+
color={
101+
isPopoverOpen ? ComponentColor.Default : ComponentColor.Tertiary
102+
}
103+
onClick={this.togglePopoverState}
104+
className={userAvatarButtonClassName}
105+
/>
106+
<FlexBox
107+
className={userPopoverClassName}
108+
direction={FlexDirection.Column}
109+
>
110+
{this.getUserPopoverContents()}
111+
</FlexBox>
112+
</div>
113+
</ClickOutside>
114+
)
115+
}
116+
}
117+
118+
export default IdentityUserAvatar
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
@import '@influxdata/clockface/dist/variables.scss';
2+
3+
.user-avatar-button {
4+
box-shadow: none !important;
5+
margin: 0;
6+
border-radius: 50%;
7+
border: 2px solid $cf-grey-65;
8+
9+
&.user-popover--open {
10+
border: none;
11+
background: linear-gradient(75.66deg, #0098f0 0%, $c-amethyst 79.64%);
12+
}
13+
}
14+
15+
.user-popover {
16+
opacity: 0;
17+
position: absolute;
18+
top: 60px;
19+
right: 32px;
20+
z-index: 9999;
21+
min-width: 250px;
22+
background: rgba(241, 241, 243, 0.1);
23+
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
24+
backdrop-filter: blur(42px);
25+
border-radius: 2px;
26+
transition: opacity 0.1s ease-in-out;
27+
28+
&.user-popover--open {
29+
opacity: 100;
30+
}
31+
32+
.user-popover-header-email {
33+
margin-top: 2px;
34+
color: $cf-grey-65;
35+
}
36+
37+
.user-popover-header {
38+
width: 100%;
39+
padding: 16px 16px 0 16px;
40+
}
41+
42+
.user-popover-footer {
43+
width: 100%;
44+
padding: 0px 16px 16px 16px;
45+
display: inline-flex;
46+
flex-direction: column;
47+
48+
.user-popover-footer--button {
49+
margin-top: 10px;
50+
cursor: pointer;
51+
color: inherit;
52+
}
53+
54+
a:hover {
55+
color: $cf-grey-75;
56+
}
57+
58+
.user-popover-footer--button-icon {
59+
font-size: 18px;
60+
color: $cf-grey-65;
61+
margin-right: 11px;
62+
}
63+
}
64+
65+
hr {
66+
background: $cf-grey-65;
67+
opacity: 20%;
68+
margin: 0;
69+
margin-top: 10px;
70+
}
71+
}

0 commit comments

Comments
 (0)