Fix: Miscellaneous UI issues (#4472)

This commit is contained in:
Sachin Chaurasiya 2022-04-25 19:21:55 +05:30 committed by GitHub
parent b75fa0fe6f
commit 02bc6f24de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 77 additions and 133 deletions

View File

@ -34,6 +34,7 @@ import {
getHtmlForNonAdminAction,
getUserTeams,
isEven,
pluralize,
} from '../../utils/CommonUtils';
import { getEntityFeedLink } from '../../utils/EntityUtils';
import { getDefaultValue } from '../../utils/FeedElementUtils';
@ -388,6 +389,14 @@ const DashboardDetails = ({
setThreadLink('');
};
const getDeleteEntityMessage = () => {
return `Deleting this ${EntityType.DASHBOARD} will also delete ${pluralize(
charts.length,
'chart',
's'
)}`;
};
const getLoader = () => {
return isentityThreadLoading ? <Loader /> : null;
};
@ -666,6 +675,7 @@ const DashboardDetails = ({
allowDelete
currentTier={tier?.tagFQN}
currentUser={owner?.id}
deletEntityMessage={getDeleteEntityMessage()}
entityId={dashboardDetails.id}
entityName={dashboardDetails.name}
entityType={EntityType.DASHBOARD}

View File

@ -153,6 +153,7 @@ jest.mock('../../utils/CommonUtils', () => ({
getHtmlForNonAdminAction: jest.fn(),
getEntityPlaceHolder: jest.fn().mockReturnValue('value'),
getEntityName: jest.fn().mockReturnValue('entityName'),
pluralize: jest.fn().mockReturnValue('2 charts'),
}));
describe('Test DashboardDetails component', () => {

View File

@ -220,7 +220,7 @@ const Users = ({
const getDisplayNameComponent = () => {
if (isAdminUser || isLoggedinUser) {
return (
<div className="tw-mt-4 tw-w-full tw-text-center">
<div className="tw-mt-4 tw-w-full">
{isDisplayNameEdit ? (
<div className="tw-flex tw-items-center tw-gap-1">
<input
@ -519,7 +519,7 @@ const Users = ({
const fetchLeftPanel = () => {
return (
<div className="tw-pt-4" data-testid="left-panel">
<div className="tw-pb-4 tw-mb-4 tw-border-b tw-flex tw-flex-col tw-items-center">
<div className="tw-pb-4 tw-mb-4 tw-border-b tw-flex tw-flex-col">
{userData.profile?.images?.image ? (
<div className="tw-h-28 tw-w-28">
<img
@ -537,7 +537,7 @@ const Users = ({
)}
{getDisplayNameComponent()}
<p className="tw-mt-2">{userData.email}</p>
{getDescriptionComponent()}
<div className="tw--ml-5">{getDescriptionComponent()}</div>
</div>
{getTeamsComponent()}
{getRolesComponent()}

View File

@ -229,6 +229,7 @@ li.ProseMirror-selectednode:after {
/* default UI Styles */
.toastui-editor-defaultUI .ProseMirror {
padding: 18px 25px;
font-size: 14px;
}
.toastui-editor-defaultUI {

View File

@ -11,12 +11,9 @@
* limitations under the License.
*/
import {
faAngleRight,
faExclamationCircle,
} from '@fortawesome/free-solid-svg-icons';
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { isString, isUndefined, startCase, uniqueId } from 'lodash';
import { isNil, isString, isUndefined, startCase, uniqueId } from 'lodash';
import { ExtraInfo } from 'Models';
import React, { FunctionComponent } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
@ -28,6 +25,7 @@ import { CurrentTourPageType } from '../../../enums/tour.enum';
import { TableType } from '../../../generated/entity/data/table';
import { TagLabel } from '../../../generated/type/tagLabel';
import { serviceTypeLogo } from '../../../utils/ServiceUtils';
import { stringToHTML } from '../../../utils/StringsUtils';
import { getEntityLink, getUsagePercentile } from '../../../utils/TableUtils';
import './TableDataCard.style.css';
import TableDataCardBody from './TableDataCardBody';
@ -67,6 +65,9 @@ const TableDataCard: FunctionComponent<Props> = ({
matches,
tableType,
deleted = false,
name,
database,
databaseSchema,
}: Props) => {
const location = useLocation();
const history = useHistory();
@ -116,25 +117,17 @@ const TableDataCard: FunctionComponent<Props> = ({
}
};
const fqnParts = fullyQualifiedName.split(FQN_SEPARATOR_CHAR);
const separatorHtml = (
<span className="tw-px-2">
<FontAwesomeIcon
className="tw-text-xs tw-cursor-default tw-text-gray-400 tw-align-middle"
icon={faAngleRight}
/>
</span>
);
const tableTitle = fqnParts.map((part, i) => {
const separator = i !== fqnParts.length - 1 ? separatorHtml : null;
return (
<span key={i}>
<span>{part}</span>
{separator}
</span>
);
});
const getTableMetaInfo = () => {
if (!isNil(database) && !isNil(databaseSchema)) {
return (
<span
className="tw-text-grey-muted tw-text-xs tw-mb-0.5"
data-testid="database-schema">{`${database}${FQN_SEPARATOR_CHAR}${databaseSchema}`}</span>
);
} else {
return null;
}
};
return (
<div
@ -142,6 +135,7 @@ const TableDataCard: FunctionComponent<Props> = ({
data-testid="table-data-card"
id={id}>
<div>
{getTableMetaInfo()}
<div className="tw-flex tw-items-center">
<img
alt=""
@ -154,7 +148,7 @@ const TableDataCard: FunctionComponent<Props> = ({
data-testid="table-link"
id={`${id}Title`}
onClick={handleLinkClick}>
<span className="table-title">{tableTitle}</span>
{stringToHTML(name)}
</button>
</h6>
{deleted && (

View File

@ -11,7 +11,7 @@
* limitations under the License.
*/
import { isEmpty, isUndefined } from 'lodash';
import { isUndefined } from 'lodash';
import { FormatedTableData } from 'Models';
import PropTypes from 'prop-types';
import React, { ReactNode } from 'react';
@ -76,18 +76,7 @@ const SearchedData: React.FC<SearchedDataProp> = ({
let name = table.name;
if (!isUndefined(table.highlight)) {
const [assetName] = Object.keys(table.highlight).filter((name) =>
ASSETS_NAME.includes(name)
);
name = !isEmpty(
table.highlight?.[assetName as keyof FormatedTableData['highlight']]
)
? (
table.highlight?.[
assetName as keyof FormatedTableData['highlight']
] as string[]
).join(' ')
: name;
name = table.highlight?.name?.join(' ') || name;
}
const matches = table.highlight

View File

@ -219,7 +219,7 @@ declare module 'Models' {
tier: string | TagLabel;
highlight?: {
description: string[];
table_name: string[];
name: string[];
};
index: string;
database?: string;

View File

@ -67,6 +67,7 @@ import {
getEntityName,
hasEditAccess,
isEven,
pluralize,
} from '../../utils/CommonUtils';
import { getInfoElements } from '../../utils/EntityUtils';
import {
@ -633,6 +634,43 @@ const ServicePage: FunctionComponent = () => {
}
};
const getDeleteEntityMessage = () => {
const service = serviceName?.slice(0, -1);
switch (serviceName) {
case ServiceCategory.DATABASE_SERVICES:
return `Deleting this ${service} will also delete ${pluralize(
instanceCount,
'database',
's'
)}`;
case ServiceCategory.MESSAGING_SERVICES:
return `Deleting this ${service} will also delete ${pluralize(
instanceCount,
'topic',
's'
)}`;
case ServiceCategory.DASHBOARD_SERVICES:
return `Deleting this ${service} will also delete ${pluralize(
instanceCount,
'dashboard',
's'
)}`;
case ServiceCategory.PIPELINE_SERVICES:
return `Deleting this ${service} will also delete ${pluralize(
instanceCount,
'pipeline',
's'
)}`;
default:
return;
}
};
useEffect(() => {
setServiceName(serviceCategory || getServiceCategoryFromType(serviceType));
}, [serviceCategory, serviceType]);
@ -968,6 +1006,7 @@ const ServicePage: FunctionComponent = () => {
hideTier
isRecursiveDelete
currentUser={serviceDetails?.owner?.id}
deletEntityMessage={getDeleteEntityMessage()}
entityId={serviceDetails?.id}
entityName={serviceDetails?.name}
entityType={serviceCategory.slice(0, -1)}

View File

@ -191,10 +191,7 @@ describe('Test Service page', () => {
'service-description'
);
const type = await findAllByTestId(container, 'service-type');
const deleteIcon = await findAllByTestId(
container,
'delete-icon-container'
);
const icon = await findAllByTestId(container, 'service-icon');
expect(tabs[0]).toHaveClass('activeCategory');
@ -203,7 +200,6 @@ describe('Test Service page', () => {
mockDatabaseService.data.data.length
);
expect(type.length).toBe(mockDatabaseService.data.data.length);
expect(deleteIcon.length).toBe(mockDatabaseService.data.data.length);
expect(icon.length).toBe(mockDatabaseService.data.data.length);
});
});

View File

@ -20,7 +20,6 @@ import { Link, useHistory } from 'react-router-dom';
import { useAuthContext } from '../../authentication/auth-provider/AuthProvider';
import { addAirflowPipeline } from '../../axiosAPIs/airflowPipelineAPI';
import {
deleteService,
getServiceDetails,
getServices,
postService,
@ -35,7 +34,6 @@ import PageContainerV1 from '../../components/containers/PageContainerV1';
import PageLayout from '../../components/containers/PageLayout';
import Loader from '../../components/Loader/Loader';
import { AddServiceModal } from '../../components/Modals/AddServiceModal/AddServiceModal';
import ConfirmationModal from '../../components/Modals/ConfirmationModal/ConfirmationModal';
import {
getServiceDetailsPath,
PAGE_SIZE,
@ -73,7 +71,6 @@ import { getDashboardURL } from '../../utils/DashboardServiceUtils';
import { getBrokers } from '../../utils/MessagingServiceUtils';
import { getAddServicePath } from '../../utils/RouterUtils';
import { getErrorText } from '../../utils/StringsUtils';
import SVGIcons from '../../utils/SvgUtils';
import { showErrorToast } from '../../utils/ToastUtils';
type ServiceRecord = {
@ -106,11 +103,6 @@ const ServicesPage = () => {
const { isAdminUser } = useAuth();
const { isAuthDisabled } = useAuthContext();
const [isModalOpen, setIsModalOpen] = useState(false);
const [isConfirmationModalOpen, setIsConfirmationModalOpen] = useState(false);
const [deleteSelection, setDeleteSelection] = useState({
id: '',
name: '',
});
const [serviceName, setServiceName] =
useState<ServiceTypes>('databaseServices');
const [paging, setPaging] = useState<ServicePagingRecord>({
@ -363,47 +355,6 @@ const ServicesPage = () => {
});
};
const handleCancelConfirmationModal = () => {
setIsConfirmationModalOpen(false);
setDeleteSelection({
id: '',
name: '',
});
};
const handleDelete = (id: string) => {
deleteService(serviceName, id)
.then((res: AxiosResponse) => {
if (res.statusText === 'OK') {
const updatedServiceList = serviceList.filter((s) => s.id !== id);
setServices({ ...services, [serviceName]: updatedServiceList });
setServicesCount((pre) => ({
...servicesCount,
[serviceName]: pre[serviceName] - 1,
}));
setServiceList(updatedServiceList);
} else {
throw jsonData['api-error-messages']['unexpected-server-response'];
}
})
.catch((err: AxiosError) => {
showErrorToast(
err,
jsonData['api-error-messages']['delete-service-error']
);
});
handleCancelConfirmationModal();
};
const ConfirmDelete = (id: string, name: string) => {
setDeleteSelection({
id,
name,
});
setIsConfirmationModalOpen(true);
};
const getServiceTabs = (): Array<{
name: ServiceTypes;
displayName: string;
@ -554,20 +505,6 @@ const ServicesPage = () => {
});
};
const getConfirmationModal = () => {
return isConfirmationModalOpen ? (
<ConfirmationModal
bodyText={`You want to delete service ${deleteSelection.name} permanently? This action cannot be reverted.`}
cancelText="Cancel"
confirmButtonCss="tw-bg-error hover:tw-bg-error focus:tw-bg-error"
confirmText="Delete"
header="Are you sure?"
onCancel={handleCancelConfirmationModal}
onConfirm={() => handleDelete(deleteSelection.id)}
/>
) : null;
};
const getAddServiceModal = () => {
return isModalOpen ? (
<AddServiceModal
@ -670,27 +607,6 @@ const ServicesPage = () => {
</div>
</div>
<div className="tw-flex tw-flex-col tw-justify-between tw-flex-none">
<div
className="tw-flex tw-justify-end"
data-testid="delete-icon-container">
<NonAdminAction
position="top"
title={TITLE_FOR_NON_ADMIN_ACTION}>
<button
className="focus:tw-outline-none"
data-testid={`delete-service-${service.name}`}
onClick={() =>
ConfirmDelete(service.id || '', service.name)
}>
<SVGIcons
alt="delete"
icon="icon-delete"
title="Delete"
width="12px"
/>
</button>
</NonAdminAction>
</div>
<div
className="tw-flex tw-justify-end"
data-testid="service-icon">
@ -741,8 +657,6 @@ const ServicesPage = () => {
{getPagination()}
{getAddServiceModal()}
{getConfirmationModal()}
</div>
</PageLayout>
);