mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-29 03:16:05 +00:00
feat(ui): Glossary name modal (#10802)
* feat: initial commit glossary redesign * chore: add localization * fix: update glossary ui * fix: missing localization * feat: update glossary ui * fix: jest tests * fix: jest tests * fix: update breadcrumbs * fix: update cypress tests * chore: remove logs * fix: update glossary right panel * fix: jest tests * fix: add reviewer functionality * feat: add entity name and entity display name rename modal * fix: add missing localization * fix: update cypress tests * fix: jest tests * fix: redesign reviewer panel * fix: remove breadcrumb sizing
This commit is contained in:
parent
0a92a897a1
commit
63edc5d5ca
@ -10,20 +10,20 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { CheckOutlined, CloseOutlined } from '@ant-design/icons';
|
import { Button, Col, Row, Space, Tooltip, Typography } from 'antd';
|
||||||
import { Button, Col, Input, Row, Space, Tooltip, Typography } from 'antd';
|
|
||||||
import DescriptionV1 from 'components/common/description/DescriptionV1';
|
import DescriptionV1 from 'components/common/description/DescriptionV1';
|
||||||
import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture';
|
import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture';
|
||||||
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
import TitleBreadcrumb from 'components/common/title-breadcrumb/title-breadcrumb.component';
|
||||||
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from 'components/common/title-breadcrumb/title-breadcrumb.interface';
|
||||||
import { UserTeamSelectableList } from 'components/common/UserTeamSelectableList/UserTeamSelectableList.component';
|
import { UserTeamSelectableList } from 'components/common/UserTeamSelectableList/UserTeamSelectableList.component';
|
||||||
|
import EntityDisplayNameModal from 'components/Modals/EntityDisplayNameModal/EntityDisplayNameModal.component';
|
||||||
|
import EntityNameModal from 'components/Modals/EntityNameModal/EntityNameModal.component';
|
||||||
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
import { OperationPermission } from 'components/PermissionProvider/PermissionProvider.interface';
|
||||||
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
|
import { FQN_SEPARATOR_CHAR } from 'constants/char.constants';
|
||||||
import { getUserPath } from 'constants/constants';
|
import { getUserPath } from 'constants/constants';
|
||||||
import { NO_PERMISSION_FOR_ACTION } from 'constants/HelperTextUtil';
|
import { NO_PERMISSION_FOR_ACTION } from 'constants/HelperTextUtil';
|
||||||
import { Glossary } from 'generated/entity/data/glossary';
|
import { Glossary } from 'generated/entity/data/glossary';
|
||||||
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
import { GlossaryTerm } from 'generated/entity/data/glossaryTerm';
|
||||||
import { useAfterMount } from 'hooks/useAfterMount';
|
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -51,40 +51,38 @@ const GlossaryHeader = ({
|
|||||||
}: GlossaryHeaderProps) => {
|
}: GlossaryHeaderProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [displayName, setDisplayName] = useState<string>();
|
|
||||||
const [isNameEditing, setIsNameEditing] = useState<boolean>(false);
|
const [isNameEditing, setIsNameEditing] = useState<boolean>(false);
|
||||||
|
const [isDisplayNameEditing, setIsDisplayNameEditing] =
|
||||||
|
useState<boolean>(false);
|
||||||
const [isDescriptionEditable, setIsDescriptionEditable] =
|
const [isDescriptionEditable, setIsDescriptionEditable] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
const [breadcrumb, setBreadcrumb] = useState<
|
const [breadcrumb, setBreadcrumb] = useState<
|
||||||
TitleBreadcrumbProps['titleLinks']
|
TitleBreadcrumbProps['titleLinks']
|
||||||
>([]);
|
>([]);
|
||||||
const [addTermButtonWidth, setAddTermButtonWidth] = useState(
|
|
||||||
document.getElementById('add-term-button')?.offsetWidth || 0
|
|
||||||
);
|
|
||||||
const [manageButtonWidth, setManageButtonWidth] = useState(
|
|
||||||
document.getElementById('manage-button')?.offsetWidth || 0
|
|
||||||
);
|
|
||||||
const [leftPanelWidth, setLeftPanelWidth] = useState(
|
|
||||||
document.getElementById('glossary-left-panel')?.offsetWidth || 0
|
|
||||||
);
|
|
||||||
|
|
||||||
const editDisplayNamePermission = useMemo(() => {
|
const editDisplayNamePermission = useMemo(() => {
|
||||||
return permissions.EditAll || permissions.EditDisplayName;
|
return permissions.EditAll || permissions.EditDisplayName;
|
||||||
}, [permissions]);
|
}, [permissions]);
|
||||||
|
|
||||||
const onDisplayNameChange = (value: string) => {
|
const onDisplayNameSave = (displayName: string) => {
|
||||||
if (selectedData.displayName !== value) {
|
|
||||||
setDisplayName(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onDisplayNameSave = () => {
|
|
||||||
let updatedDetails = cloneDeep(selectedData);
|
let updatedDetails = cloneDeep(selectedData);
|
||||||
|
|
||||||
updatedDetails = {
|
updatedDetails = {
|
||||||
...selectedData,
|
...selectedData,
|
||||||
displayName: displayName?.trim(),
|
displayName: displayName?.trim(),
|
||||||
name: displayName?.trim() || selectedData.name,
|
};
|
||||||
|
|
||||||
|
onUpdate(updatedDetails);
|
||||||
|
|
||||||
|
setIsDisplayNameEditing(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNameSave = (name: string) => {
|
||||||
|
let updatedDetails = cloneDeep(selectedData);
|
||||||
|
|
||||||
|
updatedDetails = {
|
||||||
|
...selectedData,
|
||||||
|
name: name?.trim() || selectedData.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
onUpdate(updatedDetails);
|
onUpdate(updatedDetails);
|
||||||
@ -137,70 +135,28 @@ const GlossaryHeader = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useAfterMount(() => {
|
|
||||||
setLeftPanelWidth(
|
|
||||||
document.getElementById('glossary-left-panel')?.offsetWidth || 0
|
|
||||||
);
|
|
||||||
setAddTermButtonWidth(
|
|
||||||
document.getElementById('add-term-button')?.offsetWidth || 0
|
|
||||||
);
|
|
||||||
setManageButtonWidth(
|
|
||||||
document.getElementById('manage-button')?.offsetWidth || 0
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const { displayName, fullyQualifiedName, name } = selectedData;
|
const { fullyQualifiedName, name } = selectedData;
|
||||||
setDisplayName(displayName);
|
|
||||||
if (!isGlossary) {
|
if (!isGlossary) {
|
||||||
handleBreadcrumb(fullyQualifiedName ? fullyQualifiedName : name);
|
handleBreadcrumb(fullyQualifiedName ? fullyQualifiedName : name);
|
||||||
}
|
}
|
||||||
}, [selectedData]);
|
}, [selectedData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row gutter={[0, 16]}>
|
<>
|
||||||
<Col span={24}>
|
<Row gutter={[0, 16]}>
|
||||||
<Row justify="space-between">
|
<Col span={24}>
|
||||||
<Col span={12}>
|
<Row justify="space-between">
|
||||||
{!isGlossary && (
|
<Col span={12}>
|
||||||
<div
|
{!isGlossary && (
|
||||||
className="tw-text-link tw-text-base glossary-breadcrumb"
|
<div
|
||||||
data-testid="category-name">
|
className="tw-text-link tw-text-base glossary-breadcrumb"
|
||||||
<TitleBreadcrumb
|
data-testid="category-name">
|
||||||
titleLinks={breadcrumb}
|
<TitleBreadcrumb titleLinks={breadcrumb} />
|
||||||
widthDeductions={
|
</div>
|
||||||
leftPanelWidth + addTermButtonWidth + manageButtonWidth + 20 // Additional deduction for margin on the right of leftPanel
|
)}
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isNameEditing ? (
|
|
||||||
<Space direction="horizontal">
|
|
||||||
<Input
|
|
||||||
className="input-width"
|
|
||||||
data-testid="displayName"
|
|
||||||
name="displayName"
|
|
||||||
value={displayName}
|
|
||||||
onChange={(e) => onDisplayNameChange(e.target.value)}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
data-testid="cancelAssociatedTag"
|
|
||||||
icon={<CloseOutlined />}
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
onMouseDown={() => setIsNameEditing(false)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
data-testid="saveAssociatedTag"
|
|
||||||
icon={<CheckOutlined />}
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
onMouseDown={onDisplayNameSave}
|
|
||||||
/>
|
|
||||||
</Space>
|
|
||||||
) : (
|
|
||||||
<Space direction="vertical" size={0}>
|
<Space direction="vertical" size={0}>
|
||||||
<Space>
|
<Space>
|
||||||
<Typography.Text
|
<Typography.Text
|
||||||
@ -237,7 +193,9 @@ const GlossaryHeader = ({
|
|||||||
<Tooltip
|
<Tooltip
|
||||||
title={
|
title={
|
||||||
editDisplayNamePermission
|
editDisplayNamePermission
|
||||||
? t('label.edit-entity', { entity: t('label.name') })
|
? t('label.edit-entity', {
|
||||||
|
entity: t('label.display-name'),
|
||||||
|
})
|
||||||
: NO_PERMISSION_FOR_ACTION
|
: NO_PERMISSION_FOR_ACTION
|
||||||
}>
|
}>
|
||||||
<Button
|
<Button
|
||||||
@ -248,75 +206,87 @@ const GlossaryHeader = ({
|
|||||||
}
|
}
|
||||||
size="small"
|
size="small"
|
||||||
type="text"
|
type="text"
|
||||||
onClick={() => setIsNameEditing(true)}
|
onClick={() => setIsDisplayNameEditing(true)}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Space>
|
</Space>
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
</Col>
|
||||||
</Col>
|
<Col span={12}>
|
||||||
<Col span={12}>
|
<div style={{ textAlign: 'right' }}>
|
||||||
<div style={{ textAlign: 'right' }}>
|
<GlossaryHeaderButtons
|
||||||
<GlossaryHeaderButtons
|
deleteStatus="success"
|
||||||
deleteStatus="success"
|
isGlossary={isGlossary}
|
||||||
isGlossary={isGlossary}
|
permission={permissions}
|
||||||
permission={permissions}
|
selectedData={selectedData}
|
||||||
selectedData={selectedData}
|
onEntityDelete={onDelete}
|
||||||
onEntityDelete={onDelete}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
<Col span={24}>
|
|
||||||
<Space className="flex-wrap" direction="horizontal">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Typography.Text className="text-grey-muted m-r-xs">
|
|
||||||
{`${t('label.owner')}:`}
|
|
||||||
</Typography.Text>
|
|
||||||
|
|
||||||
{selectedData.owner && getEntityName(selectedData.owner) ? (
|
|
||||||
<Space className="m-r-xss" size={4}>
|
|
||||||
<ProfilePicture
|
|
||||||
displayName={getEntityName(selectedData.owner)}
|
|
||||||
id={selectedData.owner?.id || ''}
|
|
||||||
name={selectedData.owner?.name || ''}
|
|
||||||
textClass="text-xs"
|
|
||||||
width="20"
|
|
||||||
/>
|
/>
|
||||||
<Link to={getUserPath(selectedData.owner.name ?? '')}>
|
</div>
|
||||||
{getEntityName(selectedData.owner)}
|
</Col>
|
||||||
</Link>
|
</Row>
|
||||||
</Space>
|
</Col>
|
||||||
) : (
|
<Col span={24}>
|
||||||
<span className="text-grey-muted">
|
<Space className="flex-wrap" direction="horizontal">
|
||||||
{t('label.no-entity', {
|
<div className="flex items-center">
|
||||||
entity: t('label.owner-lowercase'),
|
<Typography.Text className="text-grey-muted m-r-xs">
|
||||||
})}
|
{`${t('label.owner')}:`}
|
||||||
</span>
|
</Typography.Text>
|
||||||
)}
|
|
||||||
<div className="tw-relative">
|
{selectedData.owner && getEntityName(selectedData.owner) ? (
|
||||||
<UserTeamSelectableList
|
<Space className="m-r-xss" size={4}>
|
||||||
hasPermission={permissions.EditOwner || permissions.EditAll}
|
<ProfilePicture
|
||||||
owner={selectedData.owner}
|
displayName={getEntityName(selectedData.owner)}
|
||||||
onUpdate={handleUpdatedOwner}
|
id={selectedData.owner?.id || ''}
|
||||||
/>
|
name={selectedData.owner?.name || ''}
|
||||||
|
textClass="text-xs"
|
||||||
|
width="20"
|
||||||
|
/>
|
||||||
|
<Link to={getUserPath(selectedData.owner.name ?? '')}>
|
||||||
|
{getEntityName(selectedData.owner)}
|
||||||
|
</Link>
|
||||||
|
</Space>
|
||||||
|
) : (
|
||||||
|
<span className="text-grey-muted">
|
||||||
|
{t('label.no-entity', {
|
||||||
|
entity: t('label.owner-lowercase'),
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<div className="tw-relative">
|
||||||
|
<UserTeamSelectableList
|
||||||
|
hasPermission={permissions.EditOwner || permissions.EditAll}
|
||||||
|
owner={selectedData.owner}
|
||||||
|
onUpdate={handleUpdatedOwner}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Space>
|
||||||
</Space>
|
</Col>
|
||||||
</Col>
|
<Col data-testid="updated-by-container" span={24}>
|
||||||
<Col data-testid="updated-by-container" span={24}>
|
<DescriptionV1
|
||||||
<DescriptionV1
|
description={selectedData?.description || ''}
|
||||||
description={selectedData?.description || ''}
|
entityName={selectedData?.displayName ?? selectedData?.name}
|
||||||
entityName={selectedData?.displayName ?? selectedData?.name}
|
hasEditAccess={permissions.EditDescription || permissions.EditAll}
|
||||||
hasEditAccess={permissions.EditDescription || permissions.EditAll}
|
isEdit={isDescriptionEditable}
|
||||||
isEdit={isDescriptionEditable}
|
onCancel={() => setIsDescriptionEditable(false)}
|
||||||
onCancel={() => setIsDescriptionEditable(false)}
|
onDescriptionEdit={() => setIsDescriptionEditable(true)}
|
||||||
onDescriptionEdit={() => setIsDescriptionEditable(true)}
|
onDescriptionUpdate={onDescriptionUpdate}
|
||||||
onDescriptionUpdate={onDescriptionUpdate}
|
/>
|
||||||
/>
|
</Col>
|
||||||
</Col>
|
</Row>
|
||||||
</Row>
|
<EntityNameModal
|
||||||
|
name={selectedData.name}
|
||||||
|
visible={isNameEditing}
|
||||||
|
onCancel={() => setIsNameEditing(false)}
|
||||||
|
onSave={onNameSave}
|
||||||
|
/>
|
||||||
|
<EntityDisplayNameModal
|
||||||
|
displayName={selectedData.displayName || ''}
|
||||||
|
visible={isDisplayNameEditing}
|
||||||
|
onCancel={() => setIsDisplayNameEditing(false)}
|
||||||
|
onSave={onDisplayNameSave}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { Glossary } from 'generated/entity/data/glossary';
|
import { Glossary } from 'generated/entity/data/glossary';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from 'utils/PermissionsUtils';
|
||||||
@ -111,41 +111,6 @@ describe('GlossaryHeader component', () => {
|
|||||||
expect(screen.getByTestId('edit-name')).toBeDisabled();
|
expect(screen.getByTestId('edit-name')).toBeDisabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should show editing of name after clicking on edit icon', () => {
|
|
||||||
render(
|
|
||||||
<GlossaryHeader
|
|
||||||
isGlossary
|
|
||||||
permissions={{
|
|
||||||
...DEFAULT_ENTITY_PERMISSION,
|
|
||||||
EditAll: true,
|
|
||||||
EditDisplayName: true,
|
|
||||||
}}
|
|
||||||
selectedData={
|
|
||||||
{
|
|
||||||
displayName: 'glossaryTest',
|
|
||||||
reviewers: [
|
|
||||||
{ displayName: 'reviewer1' },
|
|
||||||
{ displayName: 'reviewer2' },
|
|
||||||
],
|
|
||||||
} as Glossary
|
|
||||||
}
|
|
||||||
onDelete={mockOnDelete}
|
|
||||||
onUpdate={mockOnUpdate}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(screen.getByTestId('edit-name')).toBeInTheDocument();
|
|
||||||
|
|
||||||
act(() => {
|
|
||||||
fireEvent.click(screen.getByTestId('edit-name'));
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.getByTestId('displayName')).toBeInTheDocument();
|
|
||||||
expect(screen.getByTestId('displayName')).toHaveValue('glossaryTest');
|
|
||||||
expect(screen.getByTestId('cancelAssociatedTag')).toBeInTheDocument();
|
|
||||||
expect(screen.getByTestId('saveAssociatedTag')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should render no owner if owner is not present', () => {
|
it('should render no owner if owner is not present', () => {
|
||||||
render(
|
render(
|
||||||
<GlossaryHeader
|
<GlossaryHeader
|
||||||
|
@ -83,6 +83,10 @@ const GlossaryHeaderButtons = ({
|
|||||||
} else {
|
} else {
|
||||||
history.push(getAddGlossaryTermsPath(glossary));
|
history.push(getAddGlossaryTermsPath(glossary));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
history.push(
|
||||||
|
getAddGlossaryTermsPath(selectedData.fullyQualifiedName ?? '')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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.
|
||||||
|
*/
|
||||||
|
import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
displayName: string;
|
||||||
|
onCancel: () => void;
|
||||||
|
onSave: (displayName: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EntityDisplayNameModal: React.FC<Props> = ({
|
||||||
|
visible,
|
||||||
|
displayName,
|
||||||
|
onCancel,
|
||||||
|
onSave,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [form] = Form.useForm<{ displayName: string }>();
|
||||||
|
|
||||||
|
const handleSave = async (obj: { displayName: string }) => {
|
||||||
|
try {
|
||||||
|
await form.validateFields();
|
||||||
|
onSave(obj.displayName);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setFieldValue('displayName', displayName);
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
destroyOnClose
|
||||||
|
footer={[
|
||||||
|
<Button key="cancel-btn" type="link" onClick={onCancel}>
|
||||||
|
{t('label.cancel')}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
data-testid="save-button"
|
||||||
|
key="save-btn"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => form.submit()}>
|
||||||
|
{t('label.save')}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
okText={t('label.save')}
|
||||||
|
title={
|
||||||
|
<Typography.Text strong data-testid="header">
|
||||||
|
{t('label.edit-glossary-display-name')}
|
||||||
|
</Typography.Text>
|
||||||
|
}
|
||||||
|
visible={visible}>
|
||||||
|
<Form form={form} layout="vertical" onFinish={handleSave}>
|
||||||
|
<Form.Item
|
||||||
|
extra={
|
||||||
|
<Typography.Text className="help-text p-x-xs tw-text-xs tw-text-grey-muted">
|
||||||
|
{t('message.edit-glossary-display-name-help')}
|
||||||
|
</Typography.Text>
|
||||||
|
}
|
||||||
|
initialValue={displayName}
|
||||||
|
label={`${t('label.display-name')}:`}
|
||||||
|
name="displayName"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: `${t('label.field-required', {
|
||||||
|
field: t('label.name'),
|
||||||
|
})}`,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Input placeholder={t('message.enter-display-name')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EntityDisplayNameModal;
|
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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.
|
||||||
|
*/
|
||||||
|
import { Button, Form, Input, Modal, Typography } from 'antd';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
visible: boolean;
|
||||||
|
name: string;
|
||||||
|
onCancel: () => void;
|
||||||
|
onSave: (name: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EntityNameModal: React.FC<Props> = ({
|
||||||
|
visible,
|
||||||
|
name,
|
||||||
|
onCancel,
|
||||||
|
onSave,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [form] = Form.useForm<{ name: string }>();
|
||||||
|
|
||||||
|
const handleSave = async (obj: { name: string }) => {
|
||||||
|
try {
|
||||||
|
await form.validateFields();
|
||||||
|
onSave(obj.name);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setFieldValue('name', name);
|
||||||
|
}, [visible]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
destroyOnClose
|
||||||
|
footer={[
|
||||||
|
<Button key="cancel-btn" type="link" onClick={onCancel}>
|
||||||
|
{t('label.cancel')}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
data-testid="save-button"
|
||||||
|
key="save-btn"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => form.submit()}>
|
||||||
|
{t('label.save')}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
okText={t('label.save')}
|
||||||
|
open={visible}
|
||||||
|
title={
|
||||||
|
<Typography.Text strong data-testid="header">
|
||||||
|
{t('label.edit-glossary-name')}
|
||||||
|
</Typography.Text>
|
||||||
|
}>
|
||||||
|
<Form form={form} layout="vertical" onFinish={handleSave}>
|
||||||
|
<Form.Item
|
||||||
|
extra={
|
||||||
|
<Typography.Text className="help-text p-x-xs m-t-xs tw-text-xs tw-text-grey-muted">
|
||||||
|
{t('message.edit-glossary-name-help')}
|
||||||
|
</Typography.Text>
|
||||||
|
}
|
||||||
|
initialValue={name}
|
||||||
|
label={`${t('label.name')}:`}
|
||||||
|
name="name"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: `${t('label.field-required', {
|
||||||
|
field: t('label.name'),
|
||||||
|
})}`,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Input
|
||||||
|
placeholder={t('label.enter-entity-name', {
|
||||||
|
entity: t('label.glossary'),
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EntityNameModal;
|
@ -245,6 +245,8 @@
|
|||||||
"edit-description-for": "Edit Description for {{entityName}}",
|
"edit-description-for": "Edit Description for {{entityName}}",
|
||||||
"edit-entity": "Edit {{entity}}",
|
"edit-entity": "Edit {{entity}}",
|
||||||
"edit-entity-name": "Edit {{entityType}}: \"{{entityName}}\"",
|
"edit-entity-name": "Edit {{entityType}}: \"{{entityName}}\"",
|
||||||
|
"edit-glossary-display-name": "Edit Glossary Display Name",
|
||||||
|
"edit-glossary-name": "Edit Glossary Name",
|
||||||
"edit-workflow-ingestion": "Edit {{workflow}} Ingestion",
|
"edit-workflow-ingestion": "Edit {{workflow}} Ingestion",
|
||||||
"edited": "Edited",
|
"edited": "Edited",
|
||||||
"effect": "Effect",
|
"effect": "Effect",
|
||||||
@ -959,6 +961,8 @@
|
|||||||
"downstream-depth-message": "Please select a value for downstream depth",
|
"downstream-depth-message": "Please select a value for downstream depth",
|
||||||
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
||||||
"drag-and-drop-files-here": "Drag & drop files here",
|
"drag-and-drop-files-here": "Drag & drop files here",
|
||||||
|
"edit-glossary-display-name-help": "Update Display Name",
|
||||||
|
"edit-glossary-name-help": "Changing Name will remove the existing tag and create new one with mentioned name",
|
||||||
"edit-service-entity-connection": "Edit {{entity}} Service Connection",
|
"edit-service-entity-connection": "Edit {{entity}} Service Connection",
|
||||||
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
||||||
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
||||||
|
@ -245,6 +245,8 @@
|
|||||||
"edit-description-for": "Mettre à Jour la Description pour {{entityName}}",
|
"edit-description-for": "Mettre à Jour la Description pour {{entityName}}",
|
||||||
"edit-entity": "Mettre à Jour {{entity}}",
|
"edit-entity": "Mettre à Jour {{entity}}",
|
||||||
"edit-entity-name": "Edit {{entityType}}: \"{{entityName}}\"",
|
"edit-entity-name": "Edit {{entityType}}: \"{{entityName}}\"",
|
||||||
|
"edit-glossary-display-name": "Edit Glossary Display Name",
|
||||||
|
"edit-glossary-name": "Edit Glossary Name",
|
||||||
"edit-workflow-ingestion": "Edit {{workflow}} Ingestion",
|
"edit-workflow-ingestion": "Edit {{workflow}} Ingestion",
|
||||||
"edited": "Edited",
|
"edited": "Edited",
|
||||||
"effect": "Effet",
|
"effect": "Effet",
|
||||||
@ -959,6 +961,8 @@
|
|||||||
"downstream-depth-message": "Please select a value for downstream depth",
|
"downstream-depth-message": "Please select a value for downstream depth",
|
||||||
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
||||||
"drag-and-drop-files-here": "Drag & drop files here",
|
"drag-and-drop-files-here": "Drag & drop files here",
|
||||||
|
"edit-glossary-display-name-help": "Update Display Name",
|
||||||
|
"edit-glossary-name-help": "Changing Name will remove the existing tag and create new one with mentioned name",
|
||||||
"edit-service-entity-connection": "Edit {{entity}} Service Connection",
|
"edit-service-entity-connection": "Edit {{entity}} Service Connection",
|
||||||
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
||||||
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
||||||
|
@ -245,6 +245,8 @@
|
|||||||
"edit-description-for": "{{entityName}}の説明を編集",
|
"edit-description-for": "{{entityName}}の説明を編集",
|
||||||
"edit-entity": "{{entity}}を編集",
|
"edit-entity": "{{entity}}を編集",
|
||||||
"edit-entity-name": "{{entityType}}: \"{{entityName}}\" を編集",
|
"edit-entity-name": "{{entityType}}: \"{{entityName}}\" を編集",
|
||||||
|
"edit-glossary-display-name": "Edit Glossary Display Name",
|
||||||
|
"edit-glossary-name": "Edit Glossary Name",
|
||||||
"edit-workflow-ingestion": "{{workflow}}インジェスチョンを編集",
|
"edit-workflow-ingestion": "{{workflow}}インジェスチョンを編集",
|
||||||
"edited": "編集済",
|
"edited": "編集済",
|
||||||
"effect": "エフェクト",
|
"effect": "エフェクト",
|
||||||
@ -959,6 +961,8 @@
|
|||||||
"downstream-depth-message": "Please select a value for downstream depth",
|
"downstream-depth-message": "Please select a value for downstream depth",
|
||||||
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
||||||
"drag-and-drop-files-here": "ここにファイルをドラッグ&ドロップ",
|
"drag-and-drop-files-here": "ここにファイルをドラッグ&ドロップ",
|
||||||
|
"edit-glossary-display-name-help": "Update Display Name",
|
||||||
|
"edit-glossary-name-help": "Changing Name will remove the existing tag and create new one with mentioned name",
|
||||||
"edit-service-entity-connection": "{{entity}}サービスとの接続を編集する",
|
"edit-service-entity-connection": "{{entity}}サービスとの接続を編集する",
|
||||||
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
||||||
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
||||||
|
@ -245,6 +245,8 @@
|
|||||||
"edit-description-for": "编辑描述 {{entityName}}",
|
"edit-description-for": "编辑描述 {{entityName}}",
|
||||||
"edit-entity": "编辑 {{entity}}",
|
"edit-entity": "编辑 {{entity}}",
|
||||||
"edit-entity-name": "编辑 {{entityType}}: \"{{entityName}}\"",
|
"edit-entity-name": "编辑 {{entityType}}: \"{{entityName}}\"",
|
||||||
|
"edit-glossary-display-name": "Edit Glossary Display Name",
|
||||||
|
"edit-glossary-name": "Edit Glossary Name",
|
||||||
"edit-workflow-ingestion": "编辑 {{workflow}} 获取",
|
"edit-workflow-ingestion": "编辑 {{workflow}} 获取",
|
||||||
"edited": "编辑",
|
"edited": "编辑",
|
||||||
"effect": "Effect",
|
"effect": "Effect",
|
||||||
@ -959,6 +961,8 @@
|
|||||||
"downstream-depth-message": "Please select a value for downstream depth",
|
"downstream-depth-message": "Please select a value for downstream depth",
|
||||||
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
"downstream-depth-tooltip": "Display up to 3 nodes of downstream lineage to identify the target (child levels).",
|
||||||
"drag-and-drop-files-here": "Drag & drop files here",
|
"drag-and-drop-files-here": "Drag & drop files here",
|
||||||
|
"edit-glossary-display-name-help": "Update Display Name",
|
||||||
|
"edit-glossary-name-help": "Changing Name will remove the existing tag and create new one with mentioned name",
|
||||||
"edit-service-entity-connection": "Edit {{entity}} Service Connection",
|
"edit-service-entity-connection": "Edit {{entity}} Service Connection",
|
||||||
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
"elastic-search-message": "Ensure that your Elasticsearch indexes are up-to-date by syncing, or recreating all indexes.",
|
||||||
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
"elasticsearch-setup": "Please follow the instructions here to set up Metadata ingestion and index them into Elasticsearch.",
|
||||||
|
@ -180,7 +180,6 @@ const GlossaryRightPanel = ({
|
|||||||
data-testid={`reviewer-${reviewer.displayName}`}
|
data-testid={`reviewer-${reviewer.displayName}`}
|
||||||
key={reviewer.name}>
|
key={reviewer.name}>
|
||||||
<UserTag
|
<UserTag
|
||||||
bordered
|
|
||||||
id={reviewer.id || ''}
|
id={reviewer.id || ''}
|
||||||
key={reviewer.name}
|
key={reviewer.name}
|
||||||
name={reviewer?.name || ''}
|
name={reviewer?.name || ''}
|
||||||
|
@ -1298,3 +1298,10 @@ div.ant-typography-ellipsis-custom {
|
|||||||
svg {
|
svg {
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Style for Antd Form Item Extra
|
||||||
|
*/
|
||||||
|
.help-text {
|
||||||
|
box-decoration-break: clone;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user