fix: ui feedbacks (#12356)

* fix: glossary rename issue

* fix: owner label redirections

* fix: feedbacks

* fix: feedbacks

* fix: alignment issues of mydata, following and recently_viewed

* fix: feedbacks

* fix: remove maxlength

* fix: unit tests

* fix unit tests

* fix: cypress tests

* fix: cypress tests

* fix: review comments
This commit is contained in:
karanh37 2023-07-12 11:07:30 +05:30 committed by GitHub
parent ab53a472d3
commit 09d1e9cadd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 96 additions and 169 deletions

View File

@ -1014,9 +1014,12 @@ export const updateOwner = () => {
cy.get('[data-testid="hiden-layer"]').should('exist').click();
interceptURL('GET', '/api/v1/users?limit=15', 'getUsers');
// Clicking on edit owner button
cy.get('[data-testid="add-user"]').should('be.visible').click();
cy.get('[data-testid="edit-owner"]').should('be.visible').click();
cy.get('.user-team-select-popover').contains('Users').click();
cy.get('[data-testid="selectable-list"]')
.eq(1)
.find(`[title="${text.trim()}"]`)
.click();

View File

@ -205,14 +205,28 @@ describe('Add and Remove Owner and Tier', () => {
verifyResponseStatusCode('@getOrganization', 200);
verifyResponseStatusCode('@teamPermission', 200);
cy.get('[data-testid="add-user"]').should('be.visible').click();
verifyResponseStatusCode('@getUsers', 200);
cy.get(`.ant-popover [title="${OWNER}"]`).should('be.visible').click();
interceptURL(
'GET',
'api/v1/search/query?q=**%20AND%20isBot:false&from=0&size=0&index=user_search_index',
'waitForUsers'
);
// Click on edit owner button
cy.get('[data-testid="edit-owner"]').click();
verifyResponseStatusCode('@waitForUsers', 200);
cy.get('.user-team-select-popover').contains('Users').click();
cy.get('[data-testid="selectable-list"]')
.eq(1)
.find(`[title="${OWNER}"]`)
.click();
verifyResponseStatusCode('@patchOwner', 200);
cy.get('[data-testid="owner-link"]')
.should('be.visible')
.should('contain', OWNER);
cy.get('[data-testid="add-user"]').should('be.visible').click();
cy.get('[data-testid="edit-owner"]').should('be.visible').click();
verifyResponseStatusCode('@getUsers', 200);
cy.get('[data-testid="remove-owner"]').should('be.visible').click();
verifyResponseStatusCode('@patchOwner', 200);

View File

@ -105,7 +105,6 @@ describe('Services page should work properly', () => {
verifyResponseStatusCode('@updateService', 200);
cy.get('[data-testid="owner-dropdown"]').should('have.text', service.Owner);
// Checking if description exists after assigning the owner
cy.get(':nth-child(1) > .link-title').click();
// need wait here

View File

@ -148,7 +148,7 @@ const CustomControls: FC<ControlProps> = ({
<SVGIcons
alt="icon-edit-lineag"
className="m--t-xss"
icon={isEditMode ? 'icon-edit-lineage-color' : 'icon-edit-lineage'}
icon={isEditMode ? 'icon-edit-lineage' : 'icon-edit-lineage-color'}
width="14"
/>
);
@ -270,8 +270,7 @@ const CustomControls: FC<ControlProps> = ({
className={classNames(
'custom-control-edit-button h-8 w-8 rounded-full p-x-xss',
{
'bg-primary': !isEditMode,
'bg-primary-hover-lite': isEditMode,
'bg-primary': isEditMode,
}
)}
data-testid="edit-lineage"

View File

@ -122,10 +122,9 @@ export const CustomEdge = ({
return (
<LineageEdgeIcon offset={3} x={edgeCenterX} y={edgeCenterY}>
<Button
className="d-flex justify-center items-center custom-edge-pipeline-button"
className="flex-center custom-edge-pipeline-button"
data-testid={dataTestId}
icon={icon}
type="primary"
onClick={(event) =>
data.isEditMode &&
data.addPipelineClick?.(event, rest as CustomEdgeData)
@ -187,7 +186,10 @@ export const CustomEdge = ({
{isColumnLineageAllowed &&
hasLabel &&
getLineageEdgeIcon(<PipelineIcon />, 'pipeline-label')}
getLineageEdgeIcon(
<PipelineIcon className="text-primary" />,
'pipeline-label'
)}
{isColumnLineageAllowed &&
isSelectedEditMode &&
getEditLineageIcon('add-pipeline', true, addPipelineClick)}

View File

@ -11,6 +11,9 @@
* limitations under the License.
*/
@import url('../../styles/variables.less');
@lineage-border: #b1b1b7;
.lineage-node-card {
.ant-card-body {
padding: 0 !important;
@ -40,7 +43,7 @@
}
.lineage-node {
border: @global-border;
border: 1px solid @lineage-border;
border-radius: 4px;
}
@ -69,10 +72,12 @@
.react-flow__node-input.selectable:hover,
.react-flow__node-output.selectable:hover,
.react-flow__node-group.selectable:hover {
border-color: @primary-color !important;
.lineage-node-handle {
border-color: @primary-color;
}
.lineage-node {
border-color: @primary-color !important;
}
}
.react-flow__node-default.selectable.selected,
@ -122,7 +127,7 @@
width: 20px;
height: 20px;
border-radius: 50%;
border-color: @border-color;
border-color: @lineage-border;
background: @white;
top: 38px; // Need to show handles on top half
}
@ -130,7 +135,7 @@
.react-flow .lineage-column-node-handle {
width: 20px;
border-radius: 0;
border-color: @border-color;
border-color: @lineage-border;
background: @white;
top: 1px;
height: 25px;

View File

@ -67,12 +67,8 @@
position: relative;
}
.custom-edge-pipeline-button {
background: @primary-color;
color: @white;
height: 32px;
width: 32px;
border-radius: 9999px;
.custom-edge-pipeline-button.ant-btn {
background-color: @body-bg-color;
}
.custom-node {
@ -136,6 +132,8 @@
.custom-control-edit-button {
display: block !important;
border: 1px solid @border-color;
background-color: @body-bg-color;
}
.custom-lineage-heading {

View File

@ -187,7 +187,7 @@ export const EntityListWithV1: FunctionComponent<AntdEntityListProp> = ({
item.fullyQualifiedName ?? ''
)}>
<Button
className="entity-button flex-center p-xss"
className="entity-button flex-center p-0 m--ml-1"
icon={
<div className="entity-button-icon m-r-xs">
{getEntityIcon(item.type || '')}

View File

@ -22,4 +22,12 @@
.entity-summary-details {
font-size: 12px;
}
.max-two-lines {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
}

View File

@ -103,7 +103,7 @@ const MyDataWidgetInternal = () => {
item.fullyQualifiedName as string
)}>
<Button
className="entity-button flex-center p-xss"
className="entity-button flex-center p-0 m--ml-1"
icon={
<div className="entity-button-icon m-r-xs">
{getEntityIcon(item.type || '')}

View File

@ -39,8 +39,8 @@ const TableDataCardBody: FunctionComponent<Props> = ({
<div className="m-b-sm description-text" data-testid="description-text">
{description.trim() ? (
<RichTextEditorPreviewer
className="max-two-lines"
markdown={description}
maxLength={150}
showReadMoreBtn={false}
/>
) : (

View File

@ -33,6 +33,7 @@ import { ReactComponent as IconOpenLock } from 'assets/svg/open-lock.svg';
import { AxiosError } from 'axios';
import classNames from 'classnames';
import { ManageButtonItemLabel } from 'components/common/ManageButtonContentItem/ManageButtonContentItem.component';
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
import TableDataCardV2 from 'components/common/table-data-card-v2/TableDataCardV2';
import { useEntityExportModalProvider } from 'components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
import {
@ -67,12 +68,7 @@ import { useHistory, useLocation } from 'react-router-dom';
import { getSuggestions } from 'rest/miscAPI';
import { exportTeam, restoreTeam } from 'rest/teamsAPI';
import AppState from '../../../AppState';
import {
getTeamAndUserDetailsPath,
getUserPath,
LIST_SIZE,
ROUTES,
} from '../../../constants/constants';
import { LIST_SIZE, ROUTES } from '../../../constants/constants';
import { ROLE_DOCS, TEAMS_DOCS } from '../../../constants/docs.constants';
import { EntityAction, EntityType } from '../../../enums/entity.enum';
import { OwnerType } from '../../../enums/user.enum';
@ -309,30 +305,7 @@ const TeamDetailsV1 = ({
[]
);
const ownerValue = useMemo(() => {
switch (currentTeam.owner?.type) {
case 'team':
return getTeamAndUserDetailsPath(currentTeam.owner?.name || '');
case 'user':
return getUserPath(currentTeam.owner?.fullyQualifiedName ?? '');
default:
return '';
}
}, [currentTeam]);
const extraInfo: ExtraInfo[] = [
{
key: 'Owner',
value: ownerValue,
placeholderText:
currentTeam?.owner?.displayName || currentTeam?.owner?.name || '',
isLink: true,
openInNewTab: false,
profileName:
currentTeam?.owner?.type === OwnerType.USER
? currentTeam?.owner?.name
: undefined,
},
...(isOrganization
? []
: [
@ -1030,7 +1003,18 @@ const TeamDetailsV1 = ({
</div>
{emailElement}
<Space size={0}>
{extraInfo.map((info, index) => (
<OwnerLabel
hasPermission={hasAccess}
owner={currentTeam?.owner}
onUpdate={updateOwner}
/>
{!isOrganization && (
<span className="tw-mx-1.5 tw-inline-block tw-text-gray-400">
{t('label.pipe-symbol')}
</span>
)}
{extraInfo.map((info) => (
<Fragment key={uniqueId()}>
<EntitySummaryDetails
allowTeamOwner={false}
@ -1048,11 +1032,6 @@ const TeamDetailsV1 = ({
entityPermissions.EditAll ? updateTeamType : undefined
}
/>
{extraInfo.length !== 1 && index < extraInfo.length - 1 ? (
<span className="tw-mx-1.5 tw-inline-block tw-text-gray-400">
{t('label.pipe-symbol')}
</span>
) : null}
</Fragment>
))}
</Space>

View File

@ -75,7 +75,7 @@ const RecentlyViewed: FunctionComponent = () => {
item.fullyQualifiedName as string
)}>
<Button
className="entity-button flex-center p-xss"
className="entity-button flex-center p-0 m--ml-1"
icon={
<div className="entity-button-icon m-r-xs">
{getEntityIcon(item.type || '')}

View File

@ -23,14 +23,7 @@
}
.right-panel-list-item {
:hover {
.entity-button-icon {
background-color: @left-sidebar-icon-hover;
}
}
.entity-button-icon {
border-radius: 50%;
height: 24px;
width: 24px;
display: flex;

View File

@ -1305,7 +1305,7 @@
"page-sub-header-for-data-quality": "Build trust in your data with quality tests and build reliable data products.",
"page-sub-header-for-databases": "Ingest metadata from the most popular database services.",
"page-sub-header-for-messagings": "Ingest metadata from the most used messaging services.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services, right from the UI.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services right from the UI.",
"page-sub-header-for-ml-models": "Ingest metadata from ML Model services through the UI.",
"page-sub-header-for-pipelines": "Ingest metadata from the most used pipeline services.",
"page-sub-header-for-policies": "Define policies with a set of rules for fine-grained access control.",

View File

@ -1305,7 +1305,7 @@
"page-sub-header-for-data-quality": "Build trust in your data with quality tests and build reliable data products.",
"page-sub-header-for-databases": "Ingest metadata from the most popular database services.",
"page-sub-header-for-messagings": "Ingest metadata from the most used messaging services.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services, right from the UI.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services right from the UI.",
"page-sub-header-for-ml-models": "Ingest metadata from ML Model services through the UI.",
"page-sub-header-for-pipelines": "Ingest metadata from the most used pipeline services.",
"page-sub-header-for-policies": "Define policies with a set of rules for fine-grained access control.",

View File

@ -1305,7 +1305,7 @@
"page-sub-header-for-data-quality": "Build trust in your data with quality tests and build reliable data products.",
"page-sub-header-for-databases": "Ingest metadata from the most popular database services.",
"page-sub-header-for-messagings": "Ingest metadata from the most used messaging services.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services, right from the UI.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services right from the UI.",
"page-sub-header-for-ml-models": "Ingest metadata from ML Model services through the UI.",
"page-sub-header-for-pipelines": "Ingest metadata from the most used pipeline services.",
"page-sub-header-for-policies": "Define policies with a set of rules for fine-grained access control.",

View File

@ -1305,7 +1305,7 @@
"page-sub-header-for-data-quality": "Build trust in your data with quality tests and build reliable data products.",
"page-sub-header-for-databases": "Ingest metadata from the most popular database services.",
"page-sub-header-for-messagings": "Ingest metadata from the most used messaging services.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services, right from the UI.",
"page-sub-header-for-metadata": "Ingest metadata from metadata services right from the UI.",
"page-sub-header-for-ml-models": "Ingest metadata from ML Model services through the UI.",
"page-sub-header-for-pipelines": "Ingest metadata from the most used pipeline services.",
"page-sub-header-for-policies": "Define policies with a set of rules for fine-grained access control.",

View File

@ -15,12 +15,14 @@
.request-badge {
&.success {
.ant-badge-count {
background-color: @success-color;
background-color: @green-3;
color: @text-color;
}
}
&.failed {
.ant-badge-count {
background-color: @error-color;
background-color: @red-3;
color: @text-color;
}
}
&.running {

View File

@ -64,16 +64,12 @@ const GlossaryPage = () => {
setIsRightPanelLoading(true);
setSelectedData(undefined);
if (glossaries.length > 0 && glossaryFqn) {
const item = glossaries.find(
(item) => (item.fullyQualifiedName ?? '') === glossaryFqn
);
return item !== undefined;
if (glossaryFqn) {
return Fqn.split(glossaryFqn).length === 1;
}
return true;
}, [glossaries, glossaryFqn]);
}, [glossaryFqn]);
const createGlossaryPermission = useMemo(
() =>
@ -208,7 +204,7 @@ const GlossaryPage = () => {
});
if (selectedData?.name !== updatedData.name) {
history.push(getGlossaryPath(response.name));
history.push(getGlossaryPath(response.fullyQualifiedName));
fetchGlossaryList();
}
} catch (error) {

View File

@ -74,10 +74,6 @@ jest.mock('components/Glossary/GlossaryV1.component', () => {
));
});
jest.mock('../../../utils/Fqn', () => ({
split: jest.fn(),
}));
jest.mock('../GlossaryLeftPanel/GlossaryLeftPanel.component', () => {
return jest
.fn()

View File

@ -17,8 +17,8 @@ import { AddTestCaseList } from 'components/AddTestCaseList/AddTestCaseList.comp
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
import Description from 'components/common/description/Description';
import ManageButton from 'components/common/entityPageInfo/ManageButton/ManageButton';
import EntitySummaryDetails from 'components/common/EntitySummaryDetails/EntitySummaryDetails';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
import PageLayoutV1 from 'components/containers/PageLayoutV1';
@ -29,10 +29,8 @@ import {
ResourceEntity,
} from 'components/PermissionProvider/PermissionProvider.interface';
import DataQualityTab from 'components/ProfilerDashboard/component/DataQualityTab';
import { EntityInfo } from 'enums/entity.enum';
import { compare } from 'fast-json-patch';
import { useAuth } from 'hooks/authHooks';
import { ExtraInfo } from 'Models';
import { DataQualityPageTabs } from 'pages/DataQuality/DataQualityPage.interface';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@ -47,18 +45,15 @@ import {
import { getEntityName } from 'utils/EntityUtils';
import { getDataQualityPagePath } from 'utils/RouterUtils';
import {
getTeamAndUserDetailsPath,
INITIAL_PAGING_VALUE,
PAGE_SIZE,
pagingObject,
} from '../../constants/constants';
import { ACTION_TYPE, ERROR_PLACEHOLDER_TYPE } from '../../enums/common.enum';
import { OwnerType } from '../../enums/user.enum';
import { TestCase } from '../../generated/tests/testCase';
import { TestSuite } from '../../generated/tests/testSuite';
import { Include } from '../../generated/type/include';
import { Paging } from '../../generated/type/paging';
import { getEntityPlaceHolder } from '../../utils/CommonUtils';
import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils';
import { showErrorToast } from '../../utils/ToastUtils';
import './TestSuiteDetailsPage.styles.less';
@ -276,28 +271,6 @@ const TestSuiteDetailsPage = () => {
}
};
const extraInfo: Array<ExtraInfo> = useMemo(
() => [
{
key: EntityInfo.OWNER,
value:
testOwner?.type === 'team'
? getTeamAndUserDetailsPath(testOwner?.name || '')
: getEntityName(testOwner) || '',
placeholderText:
getEntityPlaceHolder(
(testOwner?.displayName as string) || (testOwner?.name as string),
testOwner?.deleted
) || '',
isLink: testOwner?.type === 'team',
openInNewTab: false,
profileName:
testOwner?.type === OwnerType.USER ? testOwner?.name : undefined,
},
],
[testOwner]
);
useEffect(() => {
if (testSuitePermissions.ViewAll || testSuitePermissions.ViewBasic) {
fetchTestSuiteByName();
@ -354,15 +327,11 @@ const TestSuiteDetailsPage = () => {
</Space>
<div className="d-flex tw-gap-1 tw-mb-2 tw-mt-1 flex-wrap">
{extraInfo.map((info) => (
<span className="d-flex" data-testid={info.key} key={info.key}>
<EntitySummaryDetails
currentOwner={testSuite?.owner}
data={info}
updateOwner={hasAccess ? onUpdateOwner : undefined}
/>
</span>
))}
<OwnerLabel
hasPermission={hasAccess}
owner={testOwner}
onUpdate={onUpdateOwner}
/>
</div>
<Space>

View File

@ -105,9 +105,6 @@ describe('TestSuiteDetailsPage component', () => {
expect(
await screen.findByText('ManageButton.component')
).toBeInTheDocument();
expect(
await screen.findByText('EntitySummaryDetails.component')
).toBeInTheDocument();
expect(
await screen.findByText('Description.component')
).toBeInTheDocument();

View File

@ -570,7 +570,7 @@ const DatabaseDetails: FunctionComponent = () => {
key: EntityTabs.SCHEMA,
children: (
<Row gutter={[0, 16]} wrap={false}>
<Col className="p-t-sm p-r-sm m-l-lg" flex="auto">
<Col className="p-t-sm m-x-lg" flex="auto">
<div className="d-flex flex-col gap-4">
<DescriptionV1
description={description}
@ -592,7 +592,7 @@ const DatabaseDetails: FunctionComponent = () => {
onThreadLinkSelect={onThreadLinkSelect}
/>
<Row justify="end">
<Col>
<Col className="p-x-xss">
<Switch
checked={showDeletedSchemas}
data-testid="show-deleted"

View File

@ -27,10 +27,10 @@ import { AxiosError } from 'axios';
import AirflowMessageBanner from 'components/common/AirflowMessageBanner/AirflowMessageBanner';
import DescriptionV1 from 'components/common/description/DescriptionV1';
import ManageButton from 'components/common/entityPageInfo/ManageButton/ManageButton';
import EntitySummaryDetails from 'components/common/EntitySummaryDetails/EntitySummaryDetails';
import ErrorPlaceHolder from 'components/common/error-with-placeholder/ErrorPlaceHolder';
import ErrorPlaceHolderIngestion from 'components/common/error-with-placeholder/ErrorPlaceHolderIngestion';
import NextPrevious from 'components/common/next-previous/NextPrevious';
import { OwnerLabel } from 'components/common/OwnerLabel/OwnerLabel.component';
import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture';
import RichTextEditorPreviewer from 'components/common/rich-text-editor/RichTextEditorPreviewer';
import TestConnection from 'components/common/TestConnection/TestConnection';
@ -55,7 +55,6 @@ import { Include } from 'generated/type/include';
import { useAuth } from 'hooks/authHooks';
import { isEmpty, isNil, isUndefined, startCase, toLower } from 'lodash';
import {
ExtraInfo,
PagingWithoutTotal,
ServicesUpdateRequest,
ServiceTypes,
@ -85,7 +84,6 @@ import { getTopics } from 'rest/topicsAPI';
import { getEntityName } from 'utils/EntityUtils';
import {
getServiceDetailsPath,
getTeamAndUserDetailsPath,
NO_DATA_PLACEHOLDER,
PAGE_SIZE,
pagingObject,
@ -97,7 +95,6 @@ import {
} from '../../constants/Services.constant';
import { SearchIndex } from '../../enums/search.enum';
import { ServiceCategory } from '../../enums/service.enum';
import { OwnerType } from '../../enums/user.enum';
import { Dashboard } from '../../generated/entity/data/dashboard';
import { Database } from '../../generated/entity/data/database';
import { Mlmodel } from '../../generated/entity/data/mlmodel';
@ -259,24 +256,6 @@ const ServicePage: FunctionComponent = () => {
activeTab,
]);
const extraInfo: Array<ExtraInfo> = [
{
key: 'Owner',
value:
serviceDetails?.owner?.type === 'team'
? getTeamAndUserDetailsPath(serviceDetails?.owner?.name || '')
: serviceDetails?.owner?.name || '',
placeholderText:
serviceDetails?.owner?.displayName || serviceDetails?.owner?.name || '',
isLink: serviceDetails?.owner?.type === 'team',
openInNewTab: false,
profileName:
serviceDetails?.owner?.type === OwnerType.USER
? serviceDetails?.owner?.name
: undefined,
},
];
const isTestingDisabled = useMemo(
() =>
!servicePermission.EditAll ||
@ -1240,20 +1219,13 @@ const ServicePage: FunctionComponent = () => {
<Col span={24}>
<Space>
{extraInfo.map((info) => (
<Space data-testid={info.key} key={info.id}>
<EntitySummaryDetails
currentOwner={serviceDetails?.owner}
data={info}
updateOwner={
servicePermission.EditAll ||
servicePermission.EditOwner
? handleUpdateOwner
: undefined
}
/>
</Space>
))}
<OwnerLabel
hasPermission={
servicePermission.EditAll || servicePermission.EditOwner
}
owner={serviceDetails?.owner}
onUpdate={handleUpdateOwner}
/>
</Space>
</Col>
<Col data-testid="description-container" span={24}>

View File

@ -13,7 +13,6 @@
@import url('../variables.less');
@background-color: #ffffff;
@left-panel-border-color: rgb(229, 231, 235);
@box-shadow-color: rgba(0, 0, 0, 0.06);
@icon-color: #515151;
@ -27,10 +26,6 @@
right: unset;
}
.ant-menu-item-group:not(:first-child) {
border-top: 1px solid @box-shadow-color;
}
.ant-menu-item-group-title {
font-size: 12px;
padding: 12px 16px;