Feature: Show org user icons on homepage (#1685)

* Displaying org user icons feature is implemented

* using username if name not found in user details

* Share user icon cypress test implemented
This commit is contained in:
devrk96 2020-11-11 18:42:05 +05:30 committed by GitHub
parent a4f652ce70
commit 73869eee69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 126 additions and 51 deletions

View File

@ -0,0 +1,25 @@
/// <reference types="Cypress" />
const homePage = require("../../../locators/HomePage.json");
describe("Check if org has user icons on homepage", function() {
let orgid;
it("create org and check if user icons exists in that org on homepage", function() {
cy.NavigateToHome();
cy.generateUUID().then(uid => {
orgid = uid;
localStorage.setItem("OrgName", orgid);
cy.createOrg(orgid);
cy.get(homePage.orgList.concat(orgid).concat(")"))
.scrollIntoView()
.should("be.visible")
.within(() => {
cy.get(homePage.shareUserIcons)
.first()
.should("be.visible");
});
});
cy.LogOut();
});
});

View File

@ -61,5 +61,6 @@
"orgWebsiteInput": "[data-cy=t--org-website-input]",
"orgHeaderName": ".t--organization-header",
"leftPanelContainer": "[data-cy=t--left-panel]",
"themeText": "label div"
"themeText": "label div",
"shareUserIcons": ".org-share-user-icons"
}

View File

@ -83,12 +83,19 @@ export interface ApplicationObject {
userPermissions: string[];
}
export interface UserRoles {
name: string;
roleName: string;
username: string;
}
export interface OrganizationApplicationObject {
applications: Array<ApplicationObject>;
organization: {
id: string;
name: string;
};
userRoles: Array<UserRoles>;
}
export interface FetchUsersApplicationsOrgsResponse extends ApiResponse {
data: {

View File

@ -28,10 +28,7 @@ import FormDialogComponent from "components/editorComponents/form/FormDialogComp
import { User } from "constants/userConstants";
import { getCurrentUser } from "selectors/usersSelectors";
import CreateOrganizationForm from "pages/organization/CreateOrganizationForm";
import {
CREATE_ORGANIZATION_FORM_NAME,
CREATE_APPLICATION_FORM_NAME,
} from "constants/forms";
import { CREATE_ORGANIZATION_FORM_NAME } from "constants/forms";
import {
getOnSelectAction,
DropdownOnSelectActions,
@ -48,7 +45,7 @@ import { Classes } from "components/ads/common";
import Menu from "components/ads/Menu";
import { Position } from "@blueprintjs/core/lib/esm/common/position";
import HelpModal from "components/designSystems/appsmith/help/HelpModal";
import { UpdateApplicationPayload } from "api/ApplicationApi";
import { UpdateApplicationPayload, UserRoles } from "api/ApplicationApi";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
@ -58,6 +55,7 @@ import CenteredWrapper from "../../components/designSystems/appsmith/CenteredWra
import NoSearchImage from "../../assets/images/NoSearchResult.svg";
import { getNextEntityName } from "utils/AppsmithUtils";
import Spinner from "components/ads/Spinner";
import ProfileImage from "pages/common/ProfileImage";
const OrgDropDown = styled.div`
display: flex;
@ -193,6 +191,25 @@ const ItemWrapper = styled.div`
const StyledIcon = styled(Icon)`
margin-right: 11px;
`;
const UserImageContainer = styled.div`
display: flex;
margin-right: 8px;
div {
cursor: default;
margin-right: -6px;
width: 24px;
height: 24px;
}
div:last-child {
margin-right: 0px;
}
`;
const OrgShareUsers = styled.div`
display: flex;
align-items: center;
`;
function Item(props: {
label: string;
@ -536,7 +553,7 @@ const ApplicationsSection = (props: any) => {
} else {
organizationsListComponent = updatedOrgs.map(
(organizationObject: any, index: number) => {
const { organization, applications } = organizationObject;
const { organization, applications, userRoles } = organizationObject;
const hasManageOrgPermissions = isPermitted(
organization.userPermissions,
PERMISSION_TYPE.MANAGE_ORGANIZATION,
@ -572,15 +589,30 @@ const ApplicationsSection = (props: any) => {
PERMISSION_TYPE.INVITE_USER_TO_ORGANIZATION,
) &&
!isFetchingApplications && (
<FormDialogComponent
trigger={
<Button text={"Share"} icon={"share"} size={Size.small} />
}
canOutsideClickClose={true}
Form={OrgInviteUsersForm}
orgId={organization.id}
title={`Invite Users to ${organization.name}`}
/>
<OrgShareUsers>
<UserImageContainer>
{userRoles.map((el: UserRoles) => (
<ProfileImage
className="org-share-user-icons"
userName={el.name ? el.name : el.username}
key={el.username}
/>
))}
</UserImageContainer>
<FormDialogComponent
trigger={
<Button
text={"Share"}
icon={"share"}
size={Size.small}
/>
}
canOutsideClickClose={true}
Form={OrgInviteUsersForm}
orgId={organization.id}
title={`Invite Users to ${organization.name}`}
/>
</OrgShareUsers>
)}
</OrgDropDown>
<ApplicationCardsWrapper key={organization.id}>

View File

@ -1,8 +1,5 @@
import React, { Fragment } from "react";
import { CommonComponentProps, Classes } from "components/ads/common";
import { getInitialsAndColorCode } from "utils/AppsmithUtils";
import { useSelector } from "react-redux";
import { getThemeDetails } from "selectors/themeSelectors";
import Text, { TextType } from "components/ads/Text";
import styled, { createGlobalStyle } from "styled-components";
import { Position } from "@blueprintjs/core";
@ -15,23 +12,13 @@ import {
DropdownOnSelectActions,
} from "./CustomizedDropdown/dropdownHelpers";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import ProfileImage from "./ProfileImage";
type TagProps = CommonComponentProps & {
onClick?: (text: string) => void;
userName?: string;
};
export const ProfileImage = styled.div<{ backgroundColor?: string }>`
width: 34px;
height: 34px;
display: flex;
align-items: center;
border-radius: 50%;
justify-content: center;
cursor: pointer;
background-color: ${props => props.backgroundColor};
`;
const ProfileMenuStyle = createGlobalStyle`
.bp3-popover {
box-shadow: none;
@ -67,20 +54,8 @@ const UserInformation = styled.div`
`;
export default function ProfileDropdown(props: TagProps) {
const themeDetails = useSelector(getThemeDetails);
const Profile = <ProfileImage userName={props.userName} />;
const initialsAndColorCode = getInitialsAndColorCode(
props.userName,
themeDetails.theme.colors.appCardColors,
);
const Profile = (
<ProfileImage backgroundColor={initialsAndColorCode[1]}>
<Text type={TextType.H6} highlight>
{initialsAndColorCode[0]}
</Text>
</ProfileImage>
);
return (
<Fragment>
<ProfileMenuStyle />

View File

@ -0,0 +1,40 @@
import React from "react";
import { useSelector } from "react-redux";
import { getThemeDetails } from "selectors/themeSelectors";
import { getInitialsAndColorCode } from "utils/AppsmithUtils";
import Text, { TextType } from "components/ads/Text";
import styled from "styled-components";
export const Profile = styled.div<{ backgroundColor?: string }>`
width: 34px;
height: 34px;
display: flex;
align-items: center;
border-radius: 50%;
justify-content: center;
cursor: pointer;
background-color: ${props => props.backgroundColor};
`;
export default function ProfileImage(props: {
userName?: string;
className?: string;
}) {
const themeDetails = useSelector(getThemeDetails);
const initialsAndColorCode = getInitialsAndColorCode(
props.userName,
themeDetails.theme.colors.appCardColors,
);
return (
<Profile
backgroundColor={initialsAndColorCode[1]}
className={props.className}
>
<Text type={TextType.H6} highlight>
{initialsAndColorCode[0]}
</Text>
</Profile>
);
}

View File

@ -38,8 +38,8 @@ import { Classes } from "components/ads/common";
import Callout from "components/ads/Callout";
import { getInitialsAndColorCode } from "utils/AppsmithUtils";
import { getThemeDetails } from "selectors/themeSelectors";
import { ProfileImage } from "pages/common/ProfileDropdown";
import { scrollbarDark } from "constants/DefaultTheme";
import ProfileImage from "pages/common/ProfileImage";
const OrgInviteTitle = styled.div`
padding: 10px 0px;
@ -280,7 +280,6 @@ const OrgInviteUsersForm = (props: any) => {
);
return {
...user,
imageBackground: details[1],
initials: details[0],
};
},
@ -358,18 +357,13 @@ const OrgInviteUsersForm = (props: any) => {
username: string;
name: string;
roleName: string;
imageBackground: string;
initials: string;
}) => {
return (
<Fragment key={user.username}>
<User>
<UserInfo>
<ProfileImage backgroundColor={user.imageBackground}>
<Text type={TextType.H6} highlight>
{user.initials}
</Text>
</ProfileImage>
<ProfileImage userName={user.initials} />
<UserName>
<Text type={TextType.H5}>{user.name}</Text>
<Text type={TextType.P2}>{user.username}</Text>

View File

@ -88,6 +88,7 @@ export function* getAllApplicationSaga() {
const organizationApplication: OrganizationApplicationObject[] = response.data.organizationApplications.map(
(userOrgs: OrganizationApplicationObject) => ({
organization: userOrgs.organization,
userRoles: userOrgs.userRoles,
applications: !userOrgs.applications
? []
: userOrgs.applications.map((application: ApplicationObject) => {