Minor: Enhance Block Editor Adjust drag-and-drop handles and improve bar menu layout (#19228)

* Minor: Enhance Block Editor Adjust drag-and-drop handles and improve bar menu layout

* Refactor: Replace RichTextEditor with dynamic description fields in user and team forms

* Enhance AddNotificationPage and AddObservabilityPage to use RichTextEditor for description input

* Enhance AddNotificationPage and AddObservabilityPage to initialize RichTextEditor with alert description
This commit is contained in:
Sachin Chaurasiya 2025-01-07 20:10:09 +05:30 committed by GitHub
parent 3678d1c3d6
commit aceadb1cde
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 441 additions and 246 deletions

View File

@ -21,6 +21,7 @@
border-top-right-radius: 4px;
background-color: @menu-bg-color;
display: flex;
flex-wrap: wrap;
flex-direction: row;
padding: 8px;
border-bottom: 1px solid @format-group-separator-color;

View File

@ -197,42 +197,63 @@ export const BlockAndDragHandle = (options: BlockAndDragHandleOptions) => {
return new Plugin({
view: (view) => {
// drag handle initialization
dragHandleElement = document.createElement('div');
dragHandleElement.draggable = true;
dragHandleElement.dataset.dragHandle = '';
dragHandleElement.title = 'Drag to move\nClick to open menu';
dragHandleElement.classList.add('om-drag-handle');
dragHandleElement.addEventListener('dragstart', (e) => {
handleDragStart(e, view);
});
dragHandleElement.addEventListener('click', (e) => {
handleDragClick(e, view);
});
try {
const isBarMenu =
view.dom?.parentElement?.parentElement?.classList.contains(
'block-editor-wrapper--bar-menu'
);
hideDragHandle();
if (isBarMenu) {
return {
destroy: () => {
// do nothing
},
};
}
// block handle initialization
blockHandleElement = document.createElement('div');
blockHandleElement.draggable = false;
blockHandleElement.dataset.blockHandle = '';
blockHandleElement.title = 'Add new node';
blockHandleElement.classList.add('om-block-handle');
// drag handle initialization
dragHandleElement = document.createElement('div');
dragHandleElement.draggable = true;
dragHandleElement.dataset.dragHandle = '';
dragHandleElement.title = 'Drag to move\nClick to open menu';
dragHandleElement.classList.add('om-drag-handle');
dragHandleElement.addEventListener('dragstart', (e) => {
handleDragStart(e, view);
});
dragHandleElement.addEventListener('click', (e) => {
handleDragClick(e, view);
});
hideBlockHandle();
hideDragHandle();
view?.dom?.parentElement?.appendChild(dragHandleElement);
view?.dom?.parentElement?.appendChild(blockHandleElement);
// block handle initialization
blockHandleElement = document.createElement('div');
blockHandleElement.draggable = false;
blockHandleElement.dataset.blockHandle = '';
blockHandleElement.title = 'Add new node';
blockHandleElement.classList.add('om-block-handle');
return {
destroy: () => {
dragHandleElement?.remove?.();
dragHandleElement = null;
hideBlockHandle();
blockHandleElement?.remove?.();
blockHandleElement = null;
},
};
view?.dom?.parentElement?.appendChild(dragHandleElement);
view?.dom?.parentElement?.appendChild(blockHandleElement);
return {
destroy: () => {
dragHandleElement?.remove?.();
dragHandleElement = null;
blockHandleElement?.remove?.();
blockHandleElement = null;
},
};
} catch (error) {
return {
destroy: () => {
// do nothing
},
};
}
},
props: {
handleDOMEvents: {

View File

@ -207,11 +207,17 @@
&.block-editor-wrapper--bar-menu {
border: 1px solid #dadde6;
border-radius: 4px;
// do not show drag handle and block handle in bar menu mode
.om-drag-handle,
.om-block-handle {
display: none;
}
.om-block-editor {
max-height: 300px;
overflow: auto;
padding-left: 50px;
padding-right: 50px;
padding-left: 24px;
padding-right: 24px;
padding-top: 8px;
&:focus-visible {
outline: none;

View File

@ -48,7 +48,6 @@ import { generateFormFields } from '../../../utils/formUtils';
import { isValidJSONString } from '../../../utils/StringsUtils';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import Loader from '../../common/Loader/Loader';
import RichTextEditor from '../../common/RichTextEditor/RichTextEditor';
import { EditTestCaseModalProps } from './AddDataQualityTest.interface';
import ParameterForm from './components/ParameterForm';
@ -214,6 +213,24 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: testCase?.description ?? '',
style: {
margin: 0,
},
},
}),
[testCase?.description]
);
useEffect(() => {
if (testCase) {
fetchTestDefinitionById();
@ -317,18 +334,7 @@ const EditTestCaseModal: React.FC<EditTestCaseModalProps> = ({
{!showOnlyParameter && (
<>
<Form.Item
label={t('label.description')}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
<RichTextEditor
initialValue={testCase?.description ?? ''}
style={{
margin: 0,
}}
/>
</Form.Item>
{generateFormFields([descriptionField])}
{isComputeRowCountFieldVisible
? generateFormFields(formFields)
: null}

View File

@ -63,7 +63,6 @@ import { getEntityName } from '../../../../utils/EntityUtils';
import { generateFormFields } from '../../../../utils/formUtils';
import { generateEntityLink } from '../../../../utils/TableUtils';
import { showErrorToast } from '../../../../utils/ToastUtils';
import RichTextEditor from '../../../common/RichTextEditor/RichTextEditor';
import {
TestCaseFormProps,
TestCaseFormType,
@ -249,6 +248,24 @@ const TestCaseForm: React.FC<TestCaseFormProps> = ({
);
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: initialValue?.description ?? '',
style: {
margin: 0,
},
},
}),
[initialValue?.description]
);
useEffect(() => {
const selectedColumn = table.columns.find(
(column) => column.name === columnName
@ -418,17 +435,8 @@ const TestCaseForm: React.FC<TestCaseFormProps> = ({
getFieldValue('useDynamicAssertion') ? null : generateParamsField
}
</Form.Item>
<Form.Item
label={t('label.description')}
name="description"
trigger="onTextChange">
<RichTextEditor
initialValue={initialValue?.description || ''}
style={{
margin: 0,
}}
/>
</Form.Item>
{generateFormFields([descriptionField])}
{isComputeRowCountFieldVisible ? generateFormFields(formFields) : null}

View File

@ -15,7 +15,6 @@ import { AxiosError } from 'axios';
import { startCase, unionBy } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import RichTextEditor from '../../../components/common/RichTextEditor/RichTextEditor';
import { EntityType } from '../../../enums/entity.enum';
import { CreateTestCaseResolutionStatus } from '../../../generated/api/tests/createTestCaseResolutionStatus';
import { TestCaseFailureReasonType } from '../../../generated/tests/resolved';
@ -29,6 +28,8 @@ import { showErrorToast } from '../../../utils/ToastUtils';
import { VALIDATION_MESSAGES } from '../../../constants/constants';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { generateFormFields } from '../../../utils/formUtils';
import { TestCaseStatusModalProps } from './TestCaseStatusModal.interface';
export const TestCaseStatusModal = ({
@ -125,6 +126,36 @@ export const TestCaseStatusModal = ({
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: ['testCaseResolutionStatusDetails', 'testCaseFailureComment'],
required: true,
label: t('label.comment'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
rules: [
{
required: true,
},
],
props: {
'data-testid': 'description',
initialValue:
data?.testCaseResolutionStatusDetails?.testCaseFailureComment ?? '',
placeHolder: t('message.write-your-text', {
text: t('label.comment'),
}),
onTextChange: (value: string) =>
form.setFieldValue(
['testCaseResolutionStatusDetails', 'testCaseFailureComment'],
value
),
},
}),
[data?.testCaseResolutionStatusDetails?.testCaseFailureComment]
);
useEffect(() => {
const assignee = data?.testCaseResolutionStatusDetails?.assignee;
if (
@ -202,36 +233,7 @@ export const TestCaseStatusModal = ({
))}
</Select>
</Form.Item>
<Form.Item
label={t('label.comment')}
name={[
'testCaseResolutionStatusDetails',
'testCaseFailureComment',
]}
rules={[
{
required: true,
},
]}>
<RichTextEditor
initialValue={
data?.testCaseResolutionStatusDetails
?.testCaseFailureComment ?? ''
}
placeHolder={t('message.write-your-text', {
text: t('label.comment'),
})}
onTextChange={(value) =>
form.setFieldValue(
[
'testCaseResolutionStatusDetails',
'testCaseFailureComment',
],
value
)
}
/>
</Form.Item>
{generateFormFields([descriptionField])}
</>
)}
{statusType === TestCaseResolutionStatusTypes.Assigned && (

View File

@ -12,7 +12,7 @@
*/
import { Button, Form, Input, Space } from 'antd';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import {
@ -22,11 +22,15 @@ import {
import { NAME_FIELD_RULES } from '../../../../constants/Form.constants';
import { TabSpecificField } from '../../../../enums/entity.enum';
import { TestSuite } from '../../../../generated/tests/testSuite';
import {
FieldProp,
FieldTypes,
} from '../../../../interface/FormUtils.interface';
import { DataQualityPageTabs } from '../../../../pages/DataQuality/DataQualityPage.interface';
import { getListTestSuites } from '../../../../rest/testAPI';
import { getField } from '../../../../utils/formUtils';
import { getDataQualityPagePath } from '../../../../utils/RouterUtils';
import Loader from '../../../common/Loader/Loader';
import RichTextEditor from '../../../common/RichTextEditor/RichTextEditor';
import { AddTestSuiteFormProps } from '../TestSuiteStepper/TestSuiteStepper.interface';
const AddTestSuiteForm: React.FC<AddTestSuiteFormProps> = ({
@ -58,6 +62,21 @@ const AddTestSuiteForm: React.FC<AddTestSuiteFormProps> = ({
history.push(getDataQualityPagePath(DataQualityPageTabs.TEST_SUITES));
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: `${t('label.description')}:`,
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'test-suite-description',
initialValue: testSuite?.description ?? '',
},
}),
[testSuite?.description]
);
useEffect(() => {
fetchTestSuites();
}, []);
@ -101,14 +120,7 @@ const AddTestSuiteForm: React.FC<AddTestSuiteFormProps> = ({
})}
/>
</Form.Item>
<Form.Item label={t('label.description')} name="description">
<RichTextEditor
data-testid="test-suite-description"
initialValue={testSuite?.description ?? ''}
onTextChange={(value) => form.setFieldsValue({ description: value })}
/>
</Form.Item>
{getField(descriptionField)}
<Form.Item noStyle>
<Space className="w-full justify-end" size={16}>
<Button data-testid="cancel-button" onClick={handleCancelClick}>

View File

@ -71,6 +71,10 @@ import {
import { TagLabel } from '../../../../generated/type/tagLabel';
import { useAuth } from '../../../../hooks/authHooks';
import { useApplicationStore } from '../../../../hooks/useApplicationStore';
import {
FieldProp,
FieldTypes,
} from '../../../../interface/FormUtils.interface';
import Assignees from '../../../../pages/TasksPage/shared/Assignees';
import DescriptionTask from '../../../../pages/TasksPage/shared/DescriptionTask';
import TagsTask from '../../../../pages/TasksPage/shared/TagsTask';
@ -84,6 +88,7 @@ import { postTestCaseIncidentStatus } from '../../../../rest/incidentManagerAPI'
import { getNameFromFQN } from '../../../../utils/CommonUtils';
import EntityLink from '../../../../utils/EntityLink';
import { getEntityFQN } from '../../../../utils/FeedUtils';
import { getField } from '../../../../utils/formUtils';
import { checkPermission } from '../../../../utils/PermissionsUtils';
import { getErrorText } from '../../../../utils/StringsUtils';
import {
@ -106,7 +111,6 @@ import { useActivityFeedProvider } from '../../../ActivityFeed/ActivityFeedProvi
import InlineEdit from '../../../common/InlineEdit/InlineEdit.component';
import { OwnerLabel } from '../../../common/OwnerLabel/OwnerLabel.component';
import EntityPopOverCard from '../../../common/PopOverCard/EntityPopOverCard';
import RichTextEditor from '../../../common/RichTextEditor/RichTextEditor';
import TaskTabIncidentManagerHeader from '../TaskTabIncidentManagerHeader/TaskTabIncidentManagerHeader.component';
import './task-tab.less';
import { TaskTabProps } from './TaskTab.interface';
@ -897,6 +901,32 @@ export const TaskTab = ({
</div>
);
const descriptionField: FieldProp = useMemo(
() => ({
name: 'testCaseFailureComment',
required: true,
label: t('label.comment'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
rules: [
{
required: true,
message: t('label.field-required', {
field: t('label.comment'),
}),
},
],
props: {
'data-testid': 'description',
initialValue: '',
placeHolder: t('message.write-your-text', {
text: t('label.comment'),
}),
},
}),
[]
);
return (
<Row className="p-y-sm p-x-md" data-testid="task-tab" gutter={[0, 24]}>
<Col
@ -1000,25 +1030,7 @@ export const TaskTab = ({
))}
</Select>
</Form.Item>
<Form.Item
label={t('label.comment')}
name="testCaseFailureComment"
rules={[
{
required: true,
message: t('label.field-required', {
field: t('label.comment'),
}),
},
]}
trigger="onTextChange">
<RichTextEditor
initialValue=""
placeHolder={t('message.write-your-text', {
text: t('label.comment'),
})}
/>
</Form.Item>
{getField(descriptionField)}
</Form>
</Modal>
) : (

View File

@ -14,7 +14,7 @@
import { DatePicker, Form, Input, Modal, Space } from 'antd';
import { AxiosError } from 'axios';
import { Moment } from 'moment';
import React, { FC, useState } from 'react';
import React, { FC, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { VALIDATION_MESSAGES } from '../../../constants/constants';
import {
@ -27,7 +27,8 @@ import { getEntityFeedLink } from '../../../utils/EntityUtils';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import RichTextEditor from '../../common/RichTextEditor/RichTextEditor';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { getField } from '../../../utils/formUtils';
import './announcement-modal.less';
interface Props {
@ -96,6 +97,22 @@ const AddAnnouncementModal: FC<Props> = ({
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: `${t('label.description')}:`,
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: '',
placeHolder: t('message.write-your-announcement-lowercase'),
},
}),
[]
);
return (
<Modal
centered
@ -162,15 +179,7 @@ const AddAnnouncementModal: FC<Props> = ({
<DatePicker className="w-full" />
</Form.Item>
</Space>
<Form.Item
label={`${t('label.description')}:`}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
<RichTextEditor
placeHolder={t('message.write-your-announcement-lowercase')}
/>
</Form.Item>
{getField(descriptionField)}
</Form>
</Modal>
);

View File

@ -13,13 +13,14 @@
import { DatePicker, Form, Input, Modal, Space } from 'antd';
import moment from 'moment';
import React, { FC } from 'react';
import React, { FC, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { VALIDATION_MESSAGES } from '../../../constants/constants';
import { AnnouncementDetails } from '../../../generated/entity/feed/thread';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { getTimeZone } from '../../../utils/date-time/DateTimeUtils';
import { getField } from '../../../utils/formUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
import RichTextEditor from '../../common/RichTextEditor/RichTextEditor';
import { CreateAnnouncement } from './AddAnnouncementModal';
import './announcement-modal.less';
@ -63,6 +64,22 @@ const EditAnnouncementModal: FC<Props> = ({
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: `${t('label.description')}:`,
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: announcement.description,
placeHolder: t('message.write-your-announcement-lowercase'),
},
}),
[announcement.description]
);
return (
<Modal
centered
@ -133,15 +150,7 @@ const EditAnnouncementModal: FC<Props> = ({
<DatePicker className="w-full" />
</Form.Item>
</Space>
<Form.Item
label={`${t('label.description')}:`}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
<RichTextEditor
placeHolder={t('message.write-your-announcement-lowercase')}
/>
</Form.Item>
{getField(descriptionField)}
</Form>
</Modal>
);

View File

@ -60,7 +60,6 @@ import CopyToClipboardButton from '../../../common/CopyToClipboardButton/CopyToC
import { DomainLabel } from '../../../common/DomainLabel/DomainLabel.component';
import InlineAlert from '../../../common/InlineAlert/InlineAlert';
import Loader from '../../../common/Loader/Loader';
import RichTextEditor from '../../../common/RichTextEditor/RichTextEditor';
import TeamsSelectable from '../../Team/TeamsSelectable/TeamsSelectable';
import { CreateUserProps } from './CreateUser.interface';
@ -195,6 +194,21 @@ const CreateUser = ({
onSave(userProfile);
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: '',
},
}),
[]
);
useEffect(() => {
generateRandomPassword();
}, []);
@ -272,13 +286,8 @@ const CreateUser = ({
</Select>
</Form.Item>
)}
<Form.Item
label={t('label.description')}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
<RichTextEditor />
</Form.Item>
{getField(descriptionField)}
{!forceBot && (
<>

View File

@ -37,4 +37,15 @@
}
}
}
.markdown-parser.white {
.block-editor-wrapper .tiptap.ProseMirror.om-block-editor {
p {
color: @white;
}
ul li::before {
background-color: @white;
}
}
}
}

View File

@ -269,11 +269,10 @@ const AddNotificationPage = () => {
label={t('label.description')}
labelCol={{ span: 24 }}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
trigger="onTextChange">
<RichTextEditor
data-testid="description"
initialValue=""
initialValue={alert?.description}
/>
</Form.Item>
</Col>

View File

@ -243,11 +243,10 @@ function AddObservabilityPage() {
label={t('label.description')}
labelCol={{ span: 24 }}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
trigger="onTextChange">
<RichTextEditor
data-testid="description"
initialValue=""
initialValue={alert?.description}
/>
</Form.Item>
</Col>

View File

@ -14,12 +14,11 @@ import { Button, Form, FormProps, Space, Tooltip, Typography } from 'antd';
import { DefaultOptionType } from 'antd/lib/select';
import { AxiosError } from 'axios';
import { filter, isEmpty } from 'lodash';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AsyncSelect } from '../../components/common/AsyncSelect/AsyncSelect';
import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels';
import RichTextEditor from '../../components/common/RichTextEditor/RichTextEditor';
import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { TitleBreadcrumbProps } from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface';
import SchemaEditor from '../../components/Database/SchemaEditor/SchemaEditor';
@ -39,6 +38,7 @@ import { CreateQuery } from '../../generated/api/data/createQuery';
import { Table } from '../../generated/entity/data/table';
import { useApplicationStore } from '../../hooks/useApplicationStore';
import { useFqn } from '../../hooks/useFqn';
import { FieldProp, FieldTypes } from '../../interface/FormUtils.interface';
import { searchData } from '../../rest/miscAPI';
import { postQuery } from '../../rest/queryAPI';
import { getTableDetailsByFQN } from '../../rest/tableAPI';
@ -49,6 +49,7 @@ import {
getEntityLabel,
getEntityName,
} from '../../utils/EntityUtils';
import { getField } from '../../utils/formUtils';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
const AddQueryPage = () => {
@ -196,6 +197,26 @@ const AddQueryPage = () => {
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: `${t('label.description')}:`,
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: '',
style: {
margin: 0,
},
placeHolder: t('message.write-your-description'),
onTextChange: (value: string) => setDescription(value),
},
}),
[]
);
return (
<ResizablePanels
className="content-height-with-resizable-panel"
@ -238,16 +259,7 @@ const AddQueryPage = () => {
showCopyButton={false}
/>
</Form.Item>
<Form.Item
label={`${t('label.description')}:`}
name="description">
<RichTextEditor
initialValue={description}
placeHolder={t('message.write-your-description')}
style={{ margin: 0 }}
onTextChange={(value) => setDescription(value)}
/>
</Form.Item>
{getField(descriptionField)}
<Form.Item
label={`${t('label.query-used-in')}:`}
name="queryUsedIn">

View File

@ -34,7 +34,6 @@ import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels';
import RichTextEditor from '../../components/common/RichTextEditor/RichTextEditor';
import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { ROUTES, VALIDATION_MESSAGES } from '../../constants/constants';
import { KPI_DATE_PICKER_FORMAT } from '../../constants/DataInsight.constants';
@ -44,11 +43,13 @@ import {
KpiTargetType,
} from '../../generated/api/dataInsight/kpi/createKpiRequest';
import { Kpi } from '../../generated/dataInsight/kpi/kpi';
import { FieldProp, FieldTypes } from '../../interface/FormUtils.interface';
import { getListKPIs, postKPI } from '../../rest/KpiAPI';
import {
getDataInsightPathWithFqn,
getDisabledDates,
} from '../../utils/DataInsightUtils';
import { getField } from '../../utils/formUtils';
import {
filterChartOptions,
getDataInsightChartForKPI,
@ -158,6 +159,33 @@ const AddKPIPage = () => {
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: true,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
rules: [
{
required: true,
message: t('label.field-required', {
field: t('label.description-kpi'),
}),
},
],
props: {
'data-testid': 'description',
initialValue: '',
style: {
margin: 0,
},
placeHolder: t('message.write-your-description'),
},
}),
[]
);
useEffect(() => {
fetchKpiList();
}, []);
@ -346,24 +374,7 @@ const AddKPIPage = () => {
</Col>
</Row>
<Form.Item
label={t('label.description')}
name="description"
rules={[
{
required: true,
message: t('label.field-required', {
field: t('label.description-kpi'),
}),
},
]}
trigger="onTextChange"
valuePropName="initialValue">
<RichTextEditor
placeHolder={t('message.write-your-description')}
style={{ margin: 0 }}
/>
</Form.Item>
{getField(descriptionField)}
<Space align="center" className="w-full justify-end">
<Button

View File

@ -36,7 +36,6 @@ import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import Loader from '../../components/common/Loader/Loader';
import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels';
import RichTextEditor from '../../components/common/RichTextEditor/RichTextEditor';
import TitleBreadcrumb from '../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { ROUTES, VALIDATION_MESSAGES } from '../../constants/constants';
import { KPI_DATE_PICKER_FORMAT } from '../../constants/DataInsight.constants';
@ -45,11 +44,13 @@ import { DataInsightChart } from '../../generated/api/dataInsight/kpi/createKpiR
import { Kpi, KpiTargetType } from '../../generated/dataInsight/kpi/kpi';
import { useAuth } from '../../hooks/authHooks';
import { useFqn } from '../../hooks/useFqn';
import { FieldProp, FieldTypes } from '../../interface/FormUtils.interface';
import { getKPIByName, patchKPI } from '../../rest/KpiAPI';
import {
getDataInsightPathWithFqn,
getDisabledDates,
} from '../../utils/DataInsightUtils';
import { getField } from '../../utils/formUtils';
import {
getKPIChartType,
KPIChartOptions,
@ -192,6 +193,25 @@ const EditKPIPage = () => {
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: kpiData?.description,
style: {
margin: 0,
},
placeHolder: t('message.write-your-description'),
},
}),
[kpiData?.description]
);
useEffect(() => {
fetchKPI();
}, [kpiName]);
@ -385,16 +405,7 @@ const EditKPIPage = () => {
</Col>
</Row>
<Form.Item
label={t('label.description')}
name="description"
trigger="onTextChange"
valuePropName="initialValue">
<RichTextEditor
placeHolder={t('message.write-your-description')}
style={{ margin: 0 }}
/>
</Form.Item>
{getField(descriptionField)}
<Space align="center" className="w-full justify-end">
<Button

View File

@ -15,10 +15,9 @@ import { Button, Divider, Form, Input, Space, Typography } from 'antd';
import { AxiosError } from 'axios';
import { t } from 'i18next';
import { trim } from 'lodash';
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import ResizablePanels from '../../../components/common/ResizablePanels/ResizablePanels';
import RichTextEditor from '../../../components/common/RichTextEditor/RichTextEditor';
import TitleBreadcrumb from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { ERROR_MESSAGE } from '../../../constants/constants';
import { NAME_FIELD_RULES } from '../../../constants/Form.constants';
@ -28,8 +27,10 @@ import {
Effect,
Rule,
} from '../../../generated/api/policies/createPolicy';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { addPolicy } from '../../../rest/rolesAPIV1';
import { getIsErrorMatch } from '../../../utils/CommonUtils';
import { getField } from '../../../utils/formUtils';
import {
getPath,
getPolicyWithFqnPath,
@ -106,6 +107,26 @@ const AddPolicyPage = () => {
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: `${t('label.description')}:`,
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: '',
style: {
margin: 0,
},
placeHolder: t('message.write-your-description'),
onTextChange: (value: string) => setDescription(value),
},
}),
[]
);
return (
<ResizablePanels
className="content-height-with-resizable-panel"
@ -143,16 +164,7 @@ const AddPolicyPage = () => {
/>
</Form.Item>
<Form.Item
label={`${t('label.description')}:`}
name="description">
<RichTextEditor
initialValue={description}
placeHolder={t('message.write-your-description')}
style={{ margin: 0 }}
onTextChange={(value) => setDescription(value)}
/>
</Form.Item>
{getField(descriptionField)}
<Divider data-testid="add-rule-divider">
{t('label.add-entity', {

View File

@ -56,6 +56,8 @@ const EditRulePage = () => {
const [policy, setPolicy] = useState<Policy>({} as Policy);
const [ruleData, setRuleData] = useState<Rule>(InitialData);
const selectedRuleRef = React.useRef<Rule | undefined>(InitialData);
const breadcrumb = useMemo(
() => [
{
@ -89,6 +91,7 @@ const EditRulePage = () => {
if (data) {
setPolicy(data);
const selectedRule = data.rules.find((rule) => rule.name === ruleName);
selectedRuleRef.current = selectedRule;
setRuleData(selectedRule ?? InitialData);
} else {
setPolicy({} as Policy);
@ -183,7 +186,11 @@ const EditRulePage = () => {
}}
layout="vertical"
onFinish={handleSubmit}>
<RuleForm ruleData={ruleData} setRuleData={setRuleData} />
<RuleForm
description={selectedRuleRef.current?.description}
ruleData={ruleData}
setRuleData={setRuleData}
/>
<Space align="center" className="w-full justify-end">
<Button
data-testid="cancel-btn"

View File

@ -18,7 +18,6 @@ import { AxiosError } from 'axios';
import { capitalize, startCase, uniq, uniqBy } from 'lodash';
import React, { FC, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import RichTextEditor from '../../../components/common/RichTextEditor/RichTextEditor';
import { NAME_FIELD_RULES } from '../../../constants/Form.constants';
import {
Effect,
@ -27,11 +26,13 @@ import {
} from '../../../generated/api/policies/createPolicy';
import { ResourceDescriptor } from '../../../generated/entity/policies/accessControl/resourceDescriptor';
import { Function } from '../../../generated/type/function';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import {
getPolicyFunctions,
getPolicyResources,
validateRuleCondition,
} from '../../../rest/rolesAPIV1';
import { getField } from '../../../utils/formUtils';
import { ALL_TYPE_RESOURCE_LIST } from '../../../utils/PermissionsUtils';
import { getErrorText } from '../../../utils/StringsUtils';
import { showErrorToast } from '../../../utils/ToastUtils';
@ -41,9 +42,14 @@ const { Option } = Select;
export interface RuleFormProps {
ruleData: Rule;
setRuleData: (value: React.SetStateAction<Rule>) => void;
description?: string;
}
const RuleForm: FC<RuleFormProps> = ({ ruleData, setRuleData }) => {
const RuleForm: FC<RuleFormProps> = ({
ruleData,
setRuleData,
description,
}) => {
const { t } = useTranslation();
const [policyResources, setPolicyResources] = useState<ResourceDescriptor[]>(
[]
@ -189,6 +195,27 @@ const RuleForm: FC<RuleFormProps> = ({ ruleData, setRuleData }) => {
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'ruleDescription',
required: false,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: description,
style: {
margin: 0,
},
placeHolder: t('message.write-your-description'),
onTextChange: (value: string) =>
setRuleData((prev: Rule) => ({ ...prev, description: value })),
},
}),
[description, setRuleData]
);
useEffect(() => {
fetchPolicyResources();
fetchPolicyFunctions();
@ -214,16 +241,7 @@ const RuleForm: FC<RuleFormProps> = ({ ruleData, setRuleData }) => {
}
/>
</Form.Item>
<Form.Item label={t('label.description')} name="ruleDescription">
<RichTextEditor
initialValue={ruleData.description || ''}
placeHolder={t('message.write-your-description')}
style={{ margin: 0 }}
onTextChange={(value: string) =>
setRuleData((prev: Rule) => ({ ...prev, description: value }))
}
/>
</Form.Item>
{getField(descriptionField)}
<Form.Item
label={`${t('label.resource-plural')}:`}
name="resources"

View File

@ -15,18 +15,19 @@ import { Button, Form, Input, Select, Space, Typography } from 'antd';
import { AxiosError } from 'axios';
import { t } from 'i18next';
import { trim } from 'lodash';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import ResizablePanels from '../../../components/common/ResizablePanels/ResizablePanels';
import RichTextEditor from '../../../components/common/RichTextEditor/RichTextEditor';
import TitleBreadcrumb from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component';
import { ERROR_MESSAGE } from '../../../constants/constants';
import { NAME_FIELD_RULES } from '../../../constants/Form.constants';
import { GlobalSettingOptions } from '../../../constants/GlobalSettings.constants';
import { TabSpecificField } from '../../../enums/entity.enum';
import { Policy } from '../../../generated/entity/policies/policy';
import { FieldProp, FieldTypes } from '../../../interface/FormUtils.interface';
import { addRole, getPolicies } from '../../../rest/rolesAPIV1';
import { getIsErrorMatch } from '../../../utils/CommonUtils';
import { getField } from '../../../utils/formUtils';
import {
getPath,
getRoleWithFqnPath,
@ -107,6 +108,26 @@ const AddRolePage = () => {
}
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: `${t('label.description')}:`,
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: '',
style: {
margin: 0,
},
placeHolder: t('message.write-your-description'),
onTextChange: (value: string) => setDescription(value),
},
}),
[]
);
useEffect(() => {
fetchPolicies();
}, []);
@ -144,16 +165,8 @@ const AddRolePage = () => {
onChange={(e) => setName(e.target.value)}
/>
</Form.Item>
<Form.Item
label={`${t('label.description')}:`}
name="description">
<RichTextEditor
initialValue={description}
placeHolder={t('message.write-your-description')}
style={{ margin: 0 }}
onTextChange={(value) => setDescription(value)}
/>
</Form.Item>
{getField(descriptionField)}
<Form.Item
label={`${t('label.select-a-policy')}:`}
name="policies"

View File

@ -19,7 +19,6 @@ import { toLower, trim } from 'lodash';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { DomainLabel } from '../../components/common/DomainLabel/DomainLabel.component';
import RichTextEditor from '../../components/common/RichTextEditor/RichTextEditor';
import { VALIDATION_MESSAGES } from '../../constants/constants';
import { NAME_FIELD_RULES } from '../../constants/Form.constants';
import { EntityType } from '../../enums/entity.enum';
@ -123,6 +122,25 @@ const AddTeamForm: React.FC<AddTeamFormType> = ({
helperText: t('message.access-to-collaborate'),
};
const descriptionField: FieldProp = useMemo(
() => ({
name: 'description',
required: false,
label: t('label.description'),
id: 'root/description',
type: FieldTypes.DESCRIPTION,
props: {
'data-testid': 'description',
initialValue: '',
style: {
marginBottom: 0,
},
onTextChange: (value: string) => setDescription(value),
},
}),
[]
);
useEffect(() => {
if (visible) {
fetchAllTeams();
@ -218,18 +236,7 @@ const AddTeamForm: React.FC<AddTeamFormType> = ({
/>
</Form.Item>
{getField(isJoinableField)}
<Form.Item
label={t('label.description')}
name="description"
style={{
marginBottom: 0,
}}>
<RichTextEditor
data-testid="description"
initialValue=""
onTextChange={(value) => setDescription(value)}
/>
</Form.Item>
{getField(descriptionField)}
<div className="m-t-xs">
{getField(domainsField)}
{selectedDomain && (