Fixed issue: UI: Glossary details page improvements#6834" (#6879)

* Fixed issue: UI: Glossary details page improvements#6834"

* added data-testid

* fixed input box re-render issue

* fixed failing cypress for glossary

* addressing comments

* fixed failing cypress

* updated glossary test and cypress

* fixed glossary test
This commit is contained in:
Shailesh Parmar 2022-08-26 16:08:02 +05:30 committed by GitHub
parent 0ad7e0af1b
commit 87f8bcc4fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 351 additions and 233 deletions

View File

@ -201,6 +201,7 @@ describe('Glossary page should work properly', () => {
cy.get('@description').type(newDescription); cy.get('@description').type(newDescription);
cy.get('[data-testid="save"]').click(); cy.get('[data-testid="save"]').click();
cy.get('.tw-modal-container').should('not.exist'); cy.get('.tw-modal-container').should('not.exist');
cy.wait(1000);
cy.get('[data-testid="viewer-container"]') cy.get('[data-testid="viewer-container"]')
.contains(newDescription) .contains(newDescription)
.should('be.visible'); .should('be.visible');
@ -215,11 +216,11 @@ describe('Glossary page should work properly', () => {
cy.wait(500); cy.wait(500);
cy.get('[data-testid="inactive-link"]').contains(term).should('be.visible'); cy.get('[data-testid="inactive-link"]').contains(term).should('be.visible');
// updating synonyms cy.get('[data-testid="section-synonyms"]')
cy.get('[data-testid="edit-synonyms"]')
.scrollIntoView() .scrollIntoView()
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get('[data-testid="synonyms"]') cy.get('[data-testid="synonyms"]')
.scrollIntoView() .scrollIntoView()
.should('be.visible') .should('be.visible')
@ -228,7 +229,7 @@ describe('Glossary page should work properly', () => {
cy.get('@synonyms').type(uSynonyms); cy.get('@synonyms').type(uSynonyms);
cy.get('[data-testid="saveAssociatedTag"]').should('be.visible').click(); cy.get('[data-testid="saveAssociatedTag"]').should('be.visible').click();
cy.wait(100); cy.wait(100);
cy.get('[data-testid="synonyms-card-container"]') cy.get('[data-testid="synonyms-container"]')
.as('synonyms-container') .as('synonyms-container')
.should('be.visible'); .should('be.visible');
@ -237,16 +238,17 @@ describe('Glossary page should work properly', () => {
}); });
// updating References // updating References
cy.get('[data-testid="edit-referencfe"]').should('exist').click(); cy.get('[data-testid="section-references"] [data-testid="add-button"]')
.should('exist')
.click();
cy.get('.tw-modal-container').should('be.visible'); cy.get('.tw-modal-container').should('be.visible');
cy.get('[data-testid="references"] > :nth-child(1) > .button-comp') cy.get('[data-testid="references"] .button-comp')
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get('#name-1').should('be.visible').type(newRef.name); cy.get('#name-1').should('be.visible').type(newRef.name);
cy.get('#url-1').should('be.visible').type(newRef.url); cy.get('#url-1').should('be.visible').type(newRef.url);
cy.get('[data-testid="saveButton"]').should('be.visible').click(); cy.get('[data-testid="saveButton"]').should('be.visible').click();
cy.get('[data-testid="references-card-container"]') cy.get('[data-testid="references-container"]')
.scrollIntoView()
.contains(newRef.name) .contains(newRef.name)
.should('be.visible') .should('be.visible')
.invoke('attr', 'href') .invoke('attr', 'href')
@ -269,6 +271,7 @@ describe('Glossary page should work properly', () => {
.contains('PersonalData.Personal') .contains('PersonalData.Personal')
.should('be.visible'); .should('be.visible');
cy.wait(1000);
// updating description // updating description
cy.get('[data-testid="edit-description"]').should('be.visible').click(); cy.get('[data-testid="edit-description"]').should('be.visible').click();
cy.get('.tw-modal-container').should('be.visible'); cy.get('.tw-modal-container').should('be.visible');
@ -279,6 +282,9 @@ describe('Glossary page should work properly', () => {
cy.get('@description').type(newDescription); cy.get('@description').type(newDescription);
cy.get('[data-testid="save"]').click(); cy.get('[data-testid="save"]').click();
cy.get('.tw-modal-container').should('not.exist'); cy.get('.tw-modal-container').should('not.exist');
cy.wait(1000);
cy.get('[data-testid="viewer-container"]') cy.get('[data-testid="viewer-container"]')
.contains(newDescription) .contains(newDescription)
.should('be.visible'); .should('be.visible');
@ -335,6 +341,7 @@ describe('Glossary page should work properly', () => {
const entity = SEARCH_ENTITY_TABLE.table_3.term; const entity = SEARCH_ENTITY_TABLE.table_3.term;
// go assets tab // go assets tab
goToAssetsTab(term); goToAssetsTab(term);
cy.wait(1000);
cy.get('[data-testid="column"] > :nth-child(1) > a') cy.get('[data-testid="column"] > :nth-child(1) > a')
.contains(entity) .contains(entity)
.should('be.visible') .should('be.visible')
@ -354,8 +361,10 @@ describe('Glossary page should work properly', () => {
.scrollIntoView() .scrollIntoView()
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get('[data-testid="saveAssociatedTag"]').scrollIntoView().click(); cy.get('[data-testid="saveAssociatedTag"]').scrollIntoView().click();
//Remove the added column tag from entity
cy.get( cy.get(
':nth-child(1) > :nth-child(5) > [data-testid="tags-wrapper"] > :nth-child(1) > :nth-child(1) > [data-testid="tag-container"] > div > span.tw-text-primary > [data-testid="tags"]' ':nth-child(1) > :nth-child(5) > [data-testid="tags-wrapper"] > :nth-child(1) > :nth-child(1) > [data-testid="tag-container"] > div > span.tw-text-primary > [data-testid="tags"]'
) )
@ -367,7 +376,6 @@ describe('Glossary page should work properly', () => {
.scrollIntoView() .scrollIntoView()
.should('be.visible') .should('be.visible')
.click(); .click();
cy.get(':nth-child(1) > .css-xb97g8') cy.get(':nth-child(1) > .css-xb97g8')
.scrollIntoView() .scrollIntoView()
.should('be.visible') .should('be.visible')

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.0465 6.70756C12.7823 6.70756 12.568 6.92179 12.568 7.18605V13.8372C12.568 14.2402 12.2402 14.568 11.8372 14.568H2.16279C1.75984 14.568 1.43198 14.2402 1.43198 13.8372V4.16279C1.43198 3.75984 1.75984 3.43198 2.16279 3.43198H8.81395C9.07821 3.43198 9.29244 3.21774 9.29244 2.95349C9.29244 2.68923 9.07821 2.475 8.81395 2.475H2.16279C1.23212 2.475 0.475 3.23212 0.475 4.16279V13.8372C0.475 14.7679 1.23212 15.525 2.16279 15.525H11.8372C12.7679 15.525 13.525 14.7679 13.525 13.8372V7.18605C13.525 6.92179 13.3108 6.70756 13.0465 6.70756Z" fill="#7147E8" stroke="#7147E8" stroke-width="0.05"/>
<path d="M15.1809 1.70377L14.297 0.819838C13.8706 0.393387 13.1767 0.393387 12.7502 0.819838L5.67892 7.89112C5.61348 7.95656 5.56888 8.03991 5.55069 8.13066L5.10871 10.3404C5.07799 10.4941 5.12609 10.653 5.23693 10.7638C5.32572 10.8526 5.44532 10.9012 5.56838 10.9012C5.59898 10.9012 5.62973 10.8982 5.66029 10.8921L7.87008 10.4501C7.96083 10.4319 8.04418 10.3873 8.10962 10.3219L15.1809 3.2506C15.1809 3.2506 15.181 3.2506 15.181 3.25057C15.6074 2.82415 15.6074 2.13025 15.1809 1.70377ZM7.54707 9.55858L6.16596 9.83484L6.44222 8.45373L12.1977 2.69815L13.3026 3.80306L7.54707 9.55858ZM14.518 2.58767L13.9656 3.14013L12.8606 2.03522L13.4131 1.4828C13.474 1.42186 13.5731 1.42183 13.6341 1.48277L14.518 2.3667C14.5789 2.42761 14.5789 2.52677 14.518 2.58767Z" fill="#7147E8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="15" height="15" rx="1.5" stroke="#7147E8"/>
<path d="M11.9175 8.6532H8.65222V11.9185C8.65222 12.2777 8.35835 12.5716 7.99916 12.5716C7.63998 12.5716 7.3461 12.2777 7.3461 11.9185V8.6532H4.0808C3.72161 8.6532 3.42773 8.35932 3.42773 8.00014C3.42773 7.64096 3.72161 7.34708 4.0808 7.34708H7.3461V4.08177C7.3461 3.72259 7.63998 3.42871 7.99916 3.42871C8.35835 3.42871 8.65222 3.72259 8.65222 4.08177V7.34708H11.9175C12.2767 7.34708 12.5706 7.64096 12.5706 8.00014C12.5706 8.35932 12.2767 8.6532 11.9175 8.6532Z" fill="#7147E8"/>
</svg>

After

Width:  |  Height:  |  Size: 658 B

View File

@ -19,8 +19,7 @@ import cryptoRandomString from 'crypto-random-string-with-promisify-polyfill';
import { cloneDeep, isEqual, isNil } from 'lodash'; import { cloneDeep, isEqual, isNil } from 'lodash';
import { EditorContentRef } from 'Models'; import { EditorContentRef } from 'Models';
import React, { FunctionComponent, useCallback, useRef, useState } from 'react'; import React, { FunctionComponent, useCallback, useRef, useState } from 'react';
import { TERM_ALL } from '../../constants/constants'; import { ROUTES, TERM_ALL } from '../../constants/constants';
import { ROUTES } from '../../constants/constants';
import { import {
GlobalSettingOptions, GlobalSettingOptions,
GlobalSettingsMenuCategory, GlobalSettingsMenuCategory,

View File

@ -49,7 +49,12 @@ jest.mock('../../components/GlossaryTerms/GlossaryTermsV1.component', () => {
return jest.fn().mockReturnValue(<>Glossary-Term component</>); return jest.fn().mockReturnValue(<>Glossary-Term component</>);
}); });
jest.mock('../common/title-breadcrumb/title-breadcrumb.component', () => {
return jest.fn().mockReturnValue(<>TitleBreadcrumb</>);
});
jest.mock('antd', () => ({ jest.mock('antd', () => ({
Card: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
Col: jest.fn().mockImplementation(({ children }) => <div>{children}</div>), Col: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
Input: jest.fn().mockImplementation(({ children }) => <div>{children}</div>), Input: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
Row: jest.fn().mockImplementation(({ children }) => <div>{children}</div>), Row: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),

View File

@ -162,8 +162,9 @@ const GlossaryV1 = ({
if (selectedKey !== key) { if (selectedKey !== key) {
handleChildLoading(true); handleChildLoading(true);
handleSelectedData(key); handleSelectedData(key);
setIsNameEditing(false);
} }
setIsNameEditing(false);
}; };
const onDisplayNameChange = (value: string) => { const onDisplayNameChange = (value: string) => {
@ -349,7 +350,7 @@ const GlossaryV1 = ({
visible={showActions} visible={showActions}
onVisibleChange={setShowActions}> onVisibleChange={setShowActions}>
<Button <Button
className="tw-rounded tw-flex tw-justify-center tw-w-8 tw-h-8 glossary-manage-button tw-mb-1 tw-flex" className="tw-rounded tw-justify-center tw-w-8 tw-h-8 glossary-manage-button tw-mb-1 tw-flex"
data-testid="manage-button" data-testid="manage-button"
disabled={isHasAccess} disabled={isHasAccess}
size="small" size="small"

View File

@ -12,6 +12,7 @@
*/ */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Card as AntdCard } from 'antd';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { cloneDeep, debounce, includes, isEqual } from 'lodash'; import { cloneDeep, debounce, includes, isEqual } from 'lodash';
@ -475,15 +476,17 @@ const GlossaryDetails = ({ isHasAccess, glossary, updateGlossary }: props) => {
<div className="tw-flex tw-gap-3"> <div className="tw-flex tw-gap-3">
<div className="tw-w-9/12"> <div className="tw-w-9/12">
<div className="tw-mb-4" data-testid="description-container"> <div className="tw-mb-4" data-testid="description-container">
<DescriptionV1 <AntdCard className="glossary-card">
removeBlur <DescriptionV1
description={glossary?.description} removeBlur
entityName={glossary?.displayName ?? glossary?.name} description={glossary?.description}
isEdit={isDescriptionEditable} entityName={glossary?.displayName ?? glossary?.name}
onCancel={onCancel} isEdit={isDescriptionEditable}
onDescriptionEdit={onDescriptionEdit} onCancel={onCancel}
onDescriptionUpdate={onDescriptionUpdate} onDescriptionEdit={onDescriptionEdit}
/> onDescriptionUpdate={onDescriptionUpdate}
/>
</AntdCard>
</div> </div>
</div> </div>
<div className="tw-w-3/12 tw-px-2"> <div className="tw-w-3/12 tw-px-2">

View File

@ -42,7 +42,7 @@ jest.mock('../../components/tags-container/tags-container', () => {
return jest.fn().mockReturnValue(<>Tags-container component</>); return jest.fn().mockReturnValue(<>Tags-container component</>);
}); });
jest.mock('../../components/common/description/DescriptionV1', () => { jest.mock('../common/description/DescriptionV1', () => {
return jest.fn().mockReturnValue(<>Description component</>); return jest.fn().mockReturnValue(<>Description component</>);
}); });
@ -58,6 +58,47 @@ jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
return jest.fn().mockReturnValue(<p>RichTextEditorPreviewer</p>); return jest.fn().mockReturnValue(<p>RichTextEditorPreviewer</p>);
}); });
jest.mock('antd', () => ({
Card: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
Col: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
Row: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
Divider: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
Typography: {
Text: jest.fn().mockImplementation(({ children }) => <div>{children}</div>),
},
Space: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
Input: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
Button: jest
.fn()
.mockImplementation(({ children, ...props }) => (
<div {...props}>{children}</div>
)),
}));
const mockProps = { const mockProps = {
assetData: mockedAssetData, assetData: mockedAssetData,
currentPage: 1, currentPage: 1,

View File

@ -12,9 +12,27 @@
*/ */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
Button,
Card,
Col,
Divider,
Input,
Row,
Space,
Typography,
} from 'antd';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import classNames from 'classnames'; import classNames from 'classnames';
import { cloneDeep, includes, isEmpty, isEqual, isUndefined } from 'lodash'; import {
cloneDeep,
includes,
isEmpty,
isEqual,
isString,
isUndefined,
kebabCase,
} from 'lodash';
import { import {
EntityTags, EntityTags,
FormattedGlossaryTermData, FormattedGlossaryTermData,
@ -40,8 +58,6 @@ import {
getTagOptionsFromFQN, getTagOptionsFromFQN,
} from '../../utils/TagsUtils'; } from '../../utils/TagsUtils';
import { showErrorToast } from '../../utils/ToastUtils'; import { showErrorToast } from '../../utils/ToastUtils';
import { Button } from '../buttons/Button/Button';
import Card from '../common/Card/Card';
import DescriptionV1 from '../common/description/DescriptionV1'; import DescriptionV1 from '../common/description/DescriptionV1';
import NonAdminAction from '../common/non-admin-action/NonAdminAction'; import NonAdminAction from '../common/non-admin-action/NonAdminAction';
import ProfilePicture from '../common/ProfilePicture/ProfilePicture'; import ProfilePicture from '../common/ProfilePicture/ProfilePicture';
@ -53,6 +69,7 @@ import TagsContainer from '../tags-container/tags-container';
import TagsViewer from '../tags-viewer/tags-viewer'; import TagsViewer from '../tags-viewer/tags-viewer';
import Tags from '../tags/tags'; import Tags from '../tags/tags';
import AssetsTabs from './tabs/AssetsTabs.component'; import AssetsTabs from './tabs/AssetsTabs.component';
const { Text } = Typography;
type Props = { type Props = {
assetData: GlossaryTermAssets; assetData: GlossaryTermAssets;
@ -65,6 +82,13 @@ type Props = {
handleUserRedirection?: (name: string) => void; handleUserRedirection?: (name: string) => void;
}; };
type SummaryDetailsProps = {
title: string;
children: React.ReactElement;
setShow?: (value: React.SetStateAction<boolean>) => void;
data?: FormattedGlossaryTermData[] | TermReference[] | string;
};
const GlossaryTermsV1 = ({ const GlossaryTermsV1 = ({
assetData, assetData,
isHasAccess, isHasAccess,
@ -292,18 +316,6 @@ const GlossaryTermsV1 = ({
} }
}; };
const handleRemoveSynonym = (_e: React.MouseEvent, synonym: string) => {
if (!isUndefined(glossaryTerm.synonyms)) {
const synonyms = glossaryTerm.synonyms.filter((d) => d !== synonym);
const updatedGlossaryTerm = {
...glossaryTerm,
synonyms: synonyms,
};
setSynonyms(synonyms.join(','));
handleGlossaryTermUpdate(updatedGlossaryTerm);
}
};
const handleTagContainerClick = () => { const handleTagContainerClick = () => {
if (!isTagEditable) { if (!isTagEditable) {
fetchTags(); fetchTags();
@ -344,33 +356,15 @@ const GlossaryTermsV1 = ({
); );
}; };
const addButton = (title: string, onClick: () => void) => { const addButton = (onClick: () => void) => {
return ( return (
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}> <NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
<Button <span
className={classNames('tw-h-8 tw-rounded', { className="tw-cursor-pointer"
'tw-opacity-40': isHasAccess, data-testid="add-button"
})}
data-testid="add-related-term-button"
size="small"
theme="primary"
variant="outlined"
onClick={onClick}> onClick={onClick}>
{title} <SVGIcons alt="icon-plus-primary" icon="icon-plus-primary-outlined" />
</Button> </span>
</NonAdminAction>
);
};
const editButton = (onClick: () => void) => {
return (
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
<button
className="focus:tw-outline-none tw-text-primary"
data-testid="edit-referencfe"
onClick={onClick}>
<SVGIcons alt="edit" icon={Icons.EDIT} title="Edit" width="16px" />
</button>
</NonAdminAction> </NonAdminAction>
); );
}; };
@ -430,46 +424,52 @@ const GlossaryTermsV1 = ({
); );
}; };
const getSynonyms = (synonyms: string) => { const getSynonyms = (synonymsList: string) => {
return !isEmpty(synonyms) ? ( return !isEmpty(synonymsList) ? (
synonyms synonymsList.split(',').map((synonym, index) => (
.split(',') <>
.map((synonym) => ( {index > 0 ? <span className="tw-mr-2">,</span> : null}
<Tags <span>{synonym}</span>
editable </>
isRemovable ))
key={synonym}
removeTag={handleRemoveSynonym}
tag={synonym}
type="border"
/>
))
) : ( ) : (
<></> <></>
); );
}; };
const relatedTermActionBtn = () => { const SummaryDetail = ({
return relatedTerms.length > 0 ? ( title,
editButton(() => setShowRelatedTermsModal(true)) children,
) : ( setShow,
<></> data,
); ...props
}; }: SummaryDetailsProps) => {
const referenceActionBtn = () => {
return references.length > 0 ? (
editButton(() => setIsReferencesEditing(true))
) : (
<></>
);
};
const summaryTab = () => {
return ( return (
<div className="tw-flex tw-gap-3"> <Space direction="vertical" {...props}>
<div className="tw-w-9/12"> <Space>
<div className="tw-mb-4" data-testid="description-container"> <Text type="secondary">{title}</Text>
<div className="tw-ml-2" data-testid={`section-${kebabCase(title)}`}>
{addButton(() => setShow && setShow(true))}
</div>
</Space>
{!isString(data) && !isUndefined(data) && data.length > 0 ? (
<div
className="tw-flex"
data-testid={`${kebabCase(title)}-container`}>
{children}
</div>
) : (
<div data-testid={`${kebabCase(title)}-container`}>{children}</div>
)}
</Space>
);
};
const SummaryTab = () => {
return (
<Row gutter={16}>
<Col flex="75%">
<Card className="glossary-card">
<DescriptionV1 <DescriptionV1
removeBlur removeBlur
description={glossaryTerm.description || ''} description={glossaryTerm.description || ''}
@ -479,113 +479,94 @@ const GlossaryTermsV1 = ({
onDescriptionEdit={onDescriptionEdit} onDescriptionEdit={onDescriptionEdit}
onDescriptionUpdate={onDescriptionUpdate} onDescriptionUpdate={onDescriptionUpdate}
/> />
</div> <Divider className="m-r-1" />
<Card <SummaryDetail
action={relatedTermActionBtn()} data={relatedTerms}
className="tw-mb-4" key="related_term"
heading="Related Terms"> setShow={setShowRelatedTermsModal}
<Fragment> title="Related Terms">
{relatedTerms.length > 0 ? ( <>
<div className="tw-flex"> {relatedTerms.map((d, i) => (
{relatedTerms.map((d, i) => ( <Fragment key={i}>
<Fragment key={i}> {i > 0 && <span className="tw-mr-2">,</span>}
{i > 0 && <span className="tw-mr-1">,</span>} <span
className="link-text-info tw-flex"
data-testid={`related-term-${d?.name}`}
onClick={() => {
onRelatedTermClick?.(d.fullyQualifiedName);
}}>
<span <span
className="link-text-info tw-flex" className={classNames('tw-inline-block tw-truncate', {
data-testid={`related-term-${d?.name}`} 'tw-w-52': (d?.name as string).length > 32,
onClick={() => { })}
onRelatedTermClick?.(d.fullyQualifiedName); title={d?.name as string}>
}}> {d?.name}
<span
className={classNames('tw-inline-block tw-truncate', {
'tw-w-52': (d?.name as string).length > 32,
})}
title={d?.name as string}>
{d?.name}
</span>
</span> </span>
</Fragment> </span>
))} </Fragment>
</div> ))}
) : ( </>
addButton('Add Related Term', () => </SummaryDetail>
setShowRelatedTermsModal(true) <Divider className="m-r-1" />
)
)}
</Fragment>
</Card>
<Card className="tw-mb-4" heading="Synonyms"> <SummaryDetail
<Fragment> key="synonyms"
{isSynonymsEditing ? ( setShow={setIsSynonymsEditing}
<div className="tw-flex tw-items-center tw-gap-1"> title="Synonyms">
<input <>
className="tw-form-inputs tw-form-inputs-padding tw-py-0.5 tw-w-72" {isSynonymsEditing ? (
data-testid="synonyms" <Space>
id="synonyms" <Input
name="synonyms" autoFocus
placeholder="Enter comma seprated term" data-testid="synonyms"
type="text" id="synonyms"
value={synonyms} key="synonym-input"
onChange={handleValidation} name="synonyms"
/> placeholder="Enter comma separated term"
<div className="tw-flex tw-justify-end" data-testid="buttons"> value={synonyms}
<Button onChange={handleValidation}
className="tw-px-1 tw-py-1 tw-rounded tw-text-sm tw-mr-1" />
data-testid="cancelAssociatedTag" <Space data-testid="buttons">
size="custom" <Button
theme="primary" data-testid="cancelAssociatedTag"
variant="contained" size="small"
onMouseDown={() => setIsSynonymsEditing(false)}> type="primary"
<FontAwesomeIcon onMouseDown={() => setIsSynonymsEditing(false)}>
className="tw-w-3.5 tw-h-3.5" <FontAwesomeIcon
icon="times" className="tw-w-3.5 tw-h-3.5"
/> icon="times"
</Button> />
<Button </Button>
className="tw-px-1 tw-py-1 tw-rounded tw-text-sm" <Button
data-testid="saveAssociatedTag" data-testid="saveAssociatedTag"
size="custom" size="small"
theme="primary" type="primary"
variant="contained" onMouseDown={handleSynonymsSave}>
onMouseDown={handleSynonymsSave}> <FontAwesomeIcon
<FontAwesomeIcon className="tw-w-3.5 tw-h-3.5"
className="tw-w-3.5 tw-h-3.5" icon="check"
icon="check" />
/> </Button>
</Button> </Space>
</div> </Space>
</div> ) : (
) : ( <>{getSynonyms(synonyms)}</>
<div className="tw-flex tw-group"> )}
<NonAdminAction </>
position="right" </SummaryDetail>
title={TITLE_FOR_NON_ADMIN_ACTION}> <Divider className="m-r-1" />
<button
className="focus:tw-outline-none tw-text-primary"
data-testid="edit-synonyms"
onClick={() => setIsSynonymsEditing(true)}>
<Tags
className="tw-font-semibold"
startWith="+ "
tag="Synonym"
type="border"
/>
</button>
</NonAdminAction>
{getSynonyms(synonyms)}
</div>
)}
</Fragment>
</Card>
<Card action={referenceActionBtn()} heading="References"> <SummaryDetail
<Fragment> data={references}
{references && references.length > 0 ? ( key="references"
<div className="tw-flex"> setShow={setIsReferencesEditing}
{references.map((d, i) => ( title="References">
<>
{references &&
references.length > 0 &&
references.map((d, i) => (
<Fragment key={i}> <Fragment key={i}>
{i > 0 && <span className="tw-mr-1">,</span>} {i > 0 && <span className="tw-mr-2">,</span>}
<a <a
className="link-text-info tw-flex" className="link-text-info tw-flex"
data-testid="owner-link" data-testid="owner-link"
@ -602,19 +583,23 @@ const GlossaryTermsV1 = ({
</a> </a>
</Fragment> </Fragment>
))} ))}
</div> </>
) : ( </SummaryDetail>
addButton('Add Reference', () => setIsReferencesEditing(true))
)}
</Fragment>
</Card> </Card>
</div> </Col>
<div className="tw-px-2 tw-w-3/12"> <Col className="tw-px-10" flex="25%">
<Card action={addReviewerButton()} heading="Reviewer"> <Card
className="glossary-card right-card"
extra={addReviewerButton()}
title={
<Text strong className="p-bt-3">
Reviewer
</Text>
}>
<div>{getReviewerTabData()}</div> <div>{getReviewerTabData()}</div>
</Card> </Card>
</div> </Col>
</div> </Row>
); );
}; };
@ -699,7 +684,7 @@ const GlossaryTermsV1 = ({
/> />
<div className="tw-flex-grow tw-py-4"> <div className="tw-flex-grow tw-py-4">
{activeTab === 1 && summaryTab()} {activeTab === 1 && <SummaryTab />}
{activeTab === 2 && ( {activeTab === 2 && (
<AssetsTabs <AssetsTabs

View File

@ -52,6 +52,6 @@
} }
.rc-tree-title { .rc-tree-title {
overflow: auto; overflow-x: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Space, Typography } from 'antd';
import classNames from 'classnames'; import classNames from 'classnames';
import { isUndefined } from 'lodash'; import { isUndefined } from 'lodash';
import { EntityFieldThreads } from 'Models'; import { EntityFieldThreads } from 'Models';
@ -22,10 +23,10 @@ import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils';
import { getEntityFeedLink } from '../../../utils/EntityUtils'; import { getEntityFeedLink } from '../../../utils/EntityUtils';
import SVGIcons, { Icons } from '../../../utils/SvgUtils'; import SVGIcons, { Icons } from '../../../utils/SvgUtils';
import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor'; import { ModalWithMarkdownEditor } from '../../Modals/ModalWithMarkdownEditor/ModalWithMarkdownEditor';
import Card from '../Card/Card';
import NonAdminAction from '../non-admin-action/NonAdminAction'; import NonAdminAction from '../non-admin-action/NonAdminAction';
import PopOver from '../popover/PopOver'; import PopOver from '../popover/PopOver';
import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from '../rich-text-editor/RichTextEditorPreviewer';
const { Text } = Typography;
interface Props { interface Props {
entityName?: string; entityName?: string;
@ -45,7 +46,6 @@ interface Props {
onSuggest?: (value: string) => void; onSuggest?: (value: string) => void;
onEntityFieldSelect?: (value: string) => void; onEntityFieldSelect?: (value: string) => void;
} }
const DescriptionV1 = ({ const DescriptionV1 = ({
owner, owner,
hasEditAccess, hasEditAccess,
@ -76,7 +76,12 @@ const DescriptionV1 = ({
className="focus:tw-outline-none tw-text-primary" className="focus:tw-outline-none tw-text-primary"
data-testid="edit-description" data-testid="edit-description"
onClick={onDescriptionEdit}> onClick={onDescriptionEdit}>
<SVGIcons alt="edit" icon={Icons.EDIT} title="Edit" width="16px" /> <SVGIcons
alt="edit"
icon={Icons.IC_EDIT_PRIMARY}
title="Edit"
width="16px"
/>
</button> </button>
</NonAdminAction> </NonAdminAction>
) : ( ) : (
@ -85,27 +90,25 @@ const DescriptionV1 = ({
}; };
return ( return (
<div className="schema-description tw-relative"> <Space className="schema-description tw-flex" direction="vertical">
<Space
style={{
display: 'flex',
width: '100%',
justifyContent: 'space-between',
}}>
<Text type="secondary">Description</Text>
<div>{editButton()}</div>
</Space>
<div> <div>
<Card {description?.trim() ? (
action={editButton()} <RichTextEditorPreviewer
className="tw-relative" enableSeeMoreVariant={!removeBlur}
heading="Description"> markdown={description}
<div />
className="description tw-h-full tw-overflow-y-scroll tw-min-h-12 tw-relative" ) : (
data-testid="description" <span className="">No description </span>
id="center"> )}
{description?.trim() ? (
<RichTextEditorPreviewer
enableSeeMoreVariant={!removeBlur}
markdown={description}
/>
) : (
<span className="tw-no-description tw-p-2">No description </span>
)}
</div>
</Card>
{isEdit && ( {isEdit && (
<ModalWithMarkdownEditor <ModalWithMarkdownEditor
header={`Edit description for ${entityName}`} header={`Edit description for ${entityName}`}
@ -184,7 +187,7 @@ const DescriptionV1 = ({
)} )}
</div> </div>
) : null} ) : null}
</div> </Space>
); );
}; };

View File

@ -0,0 +1,52 @@
/*
* Copyright 2022 Collate
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.glossary-card {
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.12);
}
.m-r-1 {
margin: 1rem 0;
}
.p-lr-3 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
.left-panel-card {
border: 1px rgb(221, 227, 234) solid;
border-radius: 4px;
box-shadow: 1px 1px 8px rgb(0 0 0 / 6%);
height: 100%;
.header {
padding: 0.5rem 0.75rem;
}
.ant-card-body {
padding: 0px;
}
.ant-card-head {
border-bottom: none;
margin-bottom: 0.5rem;
.ant-card-head-title {
font-weight: 600;
font-size: 1rem;
line-height: 1.5rem;
}
}
}
.right-card {
.ant-card-head-wrapper {
padding: 0.37rem 0.13rem;
}
}

View File

@ -14,6 +14,7 @@
import 'tailwindcss/tailwind.css'; import 'tailwindcss/tailwind.css';
import '../fonts/Inter/Inter-VariableFont_slnt,wght.ttf'; import '../fonts/Inter/Inter-VariableFont_slnt,wght.ttf';
import './antd-master.less'; import './antd-master.less';
import './components/glossary.less';
import './components/step.less'; import './components/step.less';
import './fonts.css'; import './fonts.css';
import './myDataDetailsTemp.css'; import './myDataDetailsTemp.css';

View File

@ -109,9 +109,11 @@ import IconWorkflows from '../assets/svg/ic-workflows.svg';
import IconChevronDown from '../assets/svg/icon-chevron-down.svg'; import IconChevronDown from '../assets/svg/icon-chevron-down.svg';
import IconCopy from '../assets/svg/icon-copy.svg'; import IconCopy from '../assets/svg/icon-copy.svg';
import IconDown from '../assets/svg/icon-down.svg'; import IconDown from '../assets/svg/icon-down.svg';
import IcEditPrimary from '../assets/svg/icon-edit-primary.svg';
import IconInfoSecondary from '../assets/svg/icon-info.svg'; import IconInfoSecondary from '../assets/svg/icon-info.svg';
import IconKey from '../assets/svg/icon-key.svg'; import IconKey from '../assets/svg/icon-key.svg';
import IconNotNull from '../assets/svg/icon-notnull.svg'; import IconNotNull from '../assets/svg/icon-notnull.svg';
import IconPlusPrimaryOutlined from '../assets/svg/icon-plus-primary-outlined.svg';
import IconRoleGrey from '../assets/svg/icon-role-grey.svg'; import IconRoleGrey from '../assets/svg/icon-role-grey.svg';
import IconTour from '../assets/svg/icon-tour.svg'; import IconTour from '../assets/svg/icon-tour.svg';
import IconUnique from '../assets/svg/icon-unique.svg'; import IconUnique from '../assets/svg/icon-unique.svg';
@ -271,6 +273,7 @@ export const Icons = {
TOUR: 'tour', TOUR: 'tour',
ICON_PLUS: 'icon-plus', ICON_PLUS: 'icon-plus',
ICON_PLUS_PRIMERY: 'icon-plus-primary', ICON_PLUS_PRIMERY: 'icon-plus-primary',
ICON_PLUS_PRIMARY_OUTLINED: 'icon-plus-primary-outlined',
ICON_MINUS: 'icon-minus', ICON_MINUS: 'icon-minus',
TAG: 'icon-tag', TAG: 'icon-tag',
TAG_GREY: 'icon-tag-grey', TAG_GREY: 'icon-tag-grey',
@ -333,6 +336,7 @@ export const Icons = {
POLICIES: 'policies', POLICIES: 'policies',
INFO_SECONDARY: 'info-secondary', INFO_SECONDARY: 'info-secondary',
ICON_REMOVE: 'icon-remove', ICON_REMOVE: 'icon-remove',
IC_EDIT_PRIMARY: 'ic-edit-primary',
}; };
const SVGIcons: FunctionComponent<Props> = ({ const SVGIcons: FunctionComponent<Props> = ({
@ -971,6 +975,14 @@ const SVGIcons: FunctionComponent<Props> = ({
case Icons.ICON_REMOVE: case Icons.ICON_REMOVE:
IconComponent = IconRemove; IconComponent = IconRemove;
break;
case Icons.IC_EDIT_PRIMARY:
IconComponent = IcEditPrimary;
break;
case Icons.ICON_PLUS_PRIMARY_OUTLINED:
IconComponent = IconPlusPrimaryOutlined;
break; break;
case Icons.INFO_SECONDARY: case Icons.INFO_SECONDARY: