feat : markdown support for group description (#9455)

This commit is contained in:
gaurav2733 2023-12-18 12:38:30 +05:30 committed by GitHub
parent e58e2bf3be
commit b4fe451d93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 261 additions and 60 deletions

View File

@ -0,0 +1,64 @@
import React, { useState } from 'react';
import { Button, Modal, Form } from 'antd';
import styled from 'styled-components';
import { Editor } from '../shared/tabs/Documentation/components/editor/Editor';
import { ANTD_GRAY } from '../shared/constants';
type Props = {
onClose: () => void;
onSaveAboutMe: () => void;
setStagedDescription: (des: string) => void;
stagedDescription: string | undefined;
};
const StyledEditor = styled(Editor)`
border: 1px solid ${ANTD_GRAY[4]};
`;
export default function EditGroupDescriptionModal({
onClose,
onSaveAboutMe,
setStagedDescription,
stagedDescription,
}: Props) {
const [form] = Form.useForm();
const [aboutText,setAboutText] = useState(stagedDescription)
function updateDescription(description: string) {
setAboutText(aboutText)
setStagedDescription(description);
}
const saveDescription = () => {
onSaveAboutMe();
onClose();
};
return (
<Modal
width={700}
title="Edit Description"
visible
onCancel={onClose}
footer={
<>
<Button onClick={onClose} type="text">
Cancel
</Button>
<Button id="updateGroupButton" onClick={saveDescription} disabled={!stagedDescription}>
Update
</Button>
</>
}
>
<Form form={form} initialValues={{}} layout="vertical">
<Form.Item name="description" rules={[{ whitespace: true }, { min: 1, max: 500 }]} hasFeedback>
<div>
<StyledEditor content={aboutText} onChange={updateDescription} />
</div>
</Form.Item>
</Form>
</Modal>
);
}

View File

@ -16,14 +16,15 @@ import {
EmptyValue,
SocialDetails,
EditButton,
AboutSection,
AboutSectionText,
GroupsSection,
AboutSection,
} from '../shared/SidebarStyledComponents';
import GroupMembersSideBarSection from './GroupMembersSideBarSection';
import { useUserContext } from '../../context/useUserContext';
const { Paragraph } = Typography;
import StripMarkdownText, { removeMarkdown } from '../shared/components/styled/StripMarkdownText';
import { Editor } from '../shared/tabs/Documentation/components/editor/Editor';
import EditGroupDescriptionModal from './EditGroupDescriptionModal';
import { REDESIGN_COLORS } from '../shared/constants';
type SideBarData = {
photoUrl: string | undefined;
@ -80,6 +81,61 @@ const GroupTitle = styled(Typography.Title)`
}
`;
const EditIcon = styled(EditOutlined)`
cursor: pointer;
color: ${REDESIGN_COLORS.BLUE};
`;
const AddNewDescription = styled(Button)`
display: none;
margin: -4px;
width: 140px;
`;
const StyledViewer = styled(Editor)`
padding-right: 8px;
display: block;
.remirror-editor.ProseMirror {
padding: 0;
}
`;
const DescriptionContainer = styled.div`
position: relative;
display: flex;
flex-direction: column;
width: 100%;
text-align:left;
font-weight: normal;
font
min-height: 22px;
&:hover ${AddNewDescription} {
display: block;
}
& ins.diff {
background-color: #b7eb8f99;
text-decoration: none;
&:hover {
background-color: #b7eb8faa;
}
}
& del.diff {
background-color: #ffa39e99;
text-decoration: line-through;
&: hover {
background-color: #ffa39eaa;
}
}
`;
const ExpandedActions = styled.div`
height: 10px;
`;
const ReadLessText = styled(Typography.Link)`
margin-right: 4px;
`;
/**
* Responsible for reading & writing users.
*/
@ -106,7 +162,17 @@ export default function GroupInfoSidebar({ sideBarData, refetch }: Props) {
const me = useUserContext();
const canEditGroup = me?.platformPrivileges?.manageIdentities;
const [groupTitle, setGroupTitle] = useState(name);
const [expanded, setExpanded] = useState(false);
const [isUpdatingDescription, SetIsUpdatingDescription] = useState(false);
const [stagedDescription, setStagedDescription] = useState(aboutText);
const [updateName] = useUpdateNameMutation();
const overLimit = removeMarkdown(aboutText || '').length > 80;
const ABBREVIATED_LIMIT = 80;
useEffect(() => {
setStagedDescription(aboutText);
}, [aboutText]);
useEffect(() => {
setGroupTitle(groupTitle);
@ -136,12 +202,12 @@ export default function GroupInfoSidebar({ sideBarData, refetch }: Props) {
};
// About Text save
const onSaveAboutMe = (inputString) => {
const onSaveAboutMe = () => {
updateCorpGroupPropertiesMutation({
variables: {
urn: urn || '',
input: {
description: inputString,
description: stagedDescription,
},
},
})
@ -201,16 +267,65 @@ export default function GroupInfoSidebar({ sideBarData, refetch }: Props) {
</SocialDetails>
<Divider className="divider-aboutSection" />
<AboutSection>
{TITLES.about}
<AboutSectionText>
<Paragraph
editable={canEditGroup ? { onChange: onSaveAboutMe } : false}
ellipsis={{ rows: 2, expandable: true, symbol: 'Read more' }}
>
{aboutText || <EmptyValue />}
</Paragraph>
</AboutSectionText>
<Row>
<Col span={22}>{TITLES.about}</Col>
<Col span={2}>
<EditIcon onClick={() => SetIsUpdatingDescription(true)} data-testid="edit-icon" />
</Col>
</Row>
</AboutSection>
<DescriptionContainer>
{(aboutText && expanded) || !overLimit ? (
<>
{/* Read only viewer for displaying group description */}
<StyledViewer content={aboutText} readOnly />
<ExpandedActions>
{overLimit && (
<ReadLessText
onClick={() => {
setExpanded(false);
}}
>
Read Less
</ReadLessText>
)}
</ExpandedActions>
</>
) : (
<>
{/* Display abbreviated description with option to read more */}
<StripMarkdownText
limit={ABBREVIATED_LIMIT}
readMore={
<>
<Typography.Link
onClick={() => {
setExpanded(true);
}}
>
Read More
</Typography.Link>
</>
}
shouldWrap
>
{aboutText}
</StripMarkdownText>
</>
)}
</DescriptionContainer>
{/* Modal for updating group description */}
{isUpdatingDescription && (
<EditGroupDescriptionModal
onClose={() => {
SetIsUpdatingDescription(false);
setStagedDescription(aboutText);
}}
onSaveAboutMe={onSaveAboutMe}
setStagedDescription={setStagedDescription}
stagedDescription={stagedDescription}
/>
)}
<Divider className="divider-groupsSection" />
<GroupsSection>
<GroupOwnerSideBarSection ownership={ownership} urn={urn || ''} refetch={refetch} />

View File

@ -1,16 +1,23 @@
import React, { useState } from 'react';
import React, { useRef, useState } from 'react';
import { message, Button, Input, Modal, Typography, Form, Collapse } from 'antd';
import styled from 'styled-components';
import { useCreateGroupMutation } from '../../../graphql/group.generated';
import { useEnterKeyListener } from '../../shared/useEnterKeyListener';
import { validateCustomUrnId } from '../../shared/textUtil';
import analytics, { EventType } from '../../analytics';
import { CorpGroup, EntityType } from '../../../types.generated';
import { Editor as MarkdownEditor } from '../../entity/shared/tabs/Documentation/components/editor/Editor';
import { ANTD_GRAY } from '../../entity/shared/constants';
type Props = {
onClose: () => void;
onCreate: (group: CorpGroup) => void;
};
const StyledEditor = styled(MarkdownEditor)`
border: 1px solid ${ANTD_GRAY[4]};
`;
export default function CreateGroupModal({ onClose, onCreate }: Props) {
const [stagedName, setStagedName] = useState('');
const [stagedDescription, setStagedDescription] = useState('');
@ -19,7 +26,15 @@ export default function CreateGroupModal({ onClose, onCreate }: Props) {
const [createButtonEnabled, setCreateButtonEnabled] = useState(true);
const [form] = Form.useForm();
// Reference to the styled editor for handling focus
const styledEditorRef = useRef<HTMLDivElement>(null);
const onCreateGroup = () => {
// Check if the Enter key was pressed inside the styled editor to prevent unintended form submission
const isEditorNewlineKeypress =
document.activeElement !== styledEditorRef.current &&
!styledEditorRef.current?.contains(document.activeElement);
if (isEditorNewlineKeypress) {
createGroupMutation({
variables: {
input: {
@ -58,6 +73,7 @@ export default function CreateGroupModal({ onClose, onCreate }: Props) {
setStagedDescription('');
});
onClose();
}
};
// Handle the Enter press
@ -65,8 +81,13 @@ export default function CreateGroupModal({ onClose, onCreate }: Props) {
querySelectorToExecuteClick: '#createGroupButton',
});
function updateDescription(description: string) {
setStagedDescription(description);
}
return (
<Modal
width={700}
title="Create new group"
visible
onCancel={onClose}
@ -112,12 +133,11 @@ export default function CreateGroupModal({ onClose, onCreate }: Props) {
</Form.Item>
<Form.Item label={<Typography.Text strong>Description</Typography.Text>}>
<Typography.Paragraph>An optional description for your new group.</Typography.Paragraph>
<Form.Item name="description" rules={[{ whitespace: true }, { min: 1, max: 500 }]} hasFeedback>
<Input
placeholder="A description for your group"
value={stagedDescription}
onChange={(event) => setStagedDescription(event.target.value)}
/>
<Form.Item name="description" rules={[{ whitespace: true }]} hasFeedback>
{/* Styled editor for the group description */}
<div ref={styledEditorRef}>
<StyledEditor doNotFocus content={stagedDescription} onChange={updateDescription} />
</div>
</Form.Item>
</Form.Item>
<Collapse ghost>

View File

@ -72,8 +72,10 @@ describe("create and manage group", () => {
cy.focused().clear().type(`Test group EDITED ${test_id}{enter}`);
cy.waitTextVisible("Name Updated");
cy.contains(`Test group EDITED ${test_id}`).should("be.visible");
cy.contains("Test group description").find('[aria-label="edit"]').click();
cy.focused().type(" EDITED{enter}");
cy.get('[data-testid="edit-icon"]').click();
cy.waitTextVisible("Edit Description");
cy.get("#description").should("be.visible").type(" EDITED");
cy.get("#updateGroupButton").click();
cy.waitTextVisible("Changes saved.");
cy.contains("Test group description EDITED").should("be.visible");
cy.clickOptionWithText("Add Owners");