mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-27 09:55:36 +00:00
* fix(#17016): apply Read-Only Mode to All Custom and Built-In Nodes in Block Editor * test: add unit test * fix playwright test --------- Co-authored-by: Ashish Gupta <ashish@getcollate.io>
This commit is contained in:
parent
7ef90c3628
commit
8434fe2c50
@ -186,7 +186,7 @@ test.describe('Activity feed', () => {
|
|||||||
test('Update Description Task on Columns', async ({ page }) => {
|
test('Update Description Task on Columns', async ({ page }) => {
|
||||||
const firstTaskValue: TaskDetails = {
|
const firstTaskValue: TaskDetails = {
|
||||||
term: entity.entity.name,
|
term: entity.entity.name,
|
||||||
assignee: `${user.data.firstName}.${user.data.lastName}`,
|
assignee: user.responseData.name,
|
||||||
description: 'Column Description 1',
|
description: 'Column Description 1',
|
||||||
columnName: entity.entity.columns[0].name,
|
columnName: entity.entity.columns[0].name,
|
||||||
oldDescription: entity.entity.columns[0].description,
|
oldDescription: entity.entity.columns[0].description,
|
||||||
|
@ -33,7 +33,7 @@ interface BlockMenuProps {
|
|||||||
export const BlockMenu = (props: BlockMenuProps) => {
|
export const BlockMenu = (props: BlockMenuProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { editor } = props;
|
const { editor } = props;
|
||||||
const { view } = editor;
|
const { view, isEditable } = editor;
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const popup = useRef<Instance | null>(null);
|
const popup = useRef<Instance | null>(null);
|
||||||
|
|
||||||
@ -127,7 +127,10 @@ export const BlockMenu = (props: BlockMenuProps) => {
|
|||||||
}, [editor]);
|
}, [editor]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (menuRef.current) {
|
/**
|
||||||
|
* Create a new tippy instance for the block menu if the editor is editable
|
||||||
|
*/
|
||||||
|
if (menuRef.current && isEditable) {
|
||||||
menuRef.current.remove();
|
menuRef.current.remove();
|
||||||
menuRef.current.style.visibility = 'visible';
|
menuRef.current.style.visibility = 'visible';
|
||||||
|
|
||||||
@ -150,7 +153,7 @@ export const BlockMenu = (props: BlockMenuProps) => {
|
|||||||
popup.current?.destroy();
|
popup.current?.destroy();
|
||||||
popup.current = null;
|
popup.current = null;
|
||||||
};
|
};
|
||||||
}, []);
|
}, [isEditable]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('click', handleClickDragHandle);
|
document.addEventListener('click', handleClickDragHandle);
|
||||||
|
@ -111,12 +111,14 @@ const BubbleMenu: FC<BubbleMenuProps> = ({ editor, toggleLink }) => {
|
|||||||
// - the selection is empty
|
// - the selection is empty
|
||||||
// - the selection is a node selection (for drag handles)
|
// - the selection is a node selection (for drag handles)
|
||||||
// - link is active
|
// - link is active
|
||||||
|
// - editor is not editable
|
||||||
if (
|
if (
|
||||||
editor.isActive('image') ||
|
editor.isActive('image') ||
|
||||||
empty ||
|
empty ||
|
||||||
isNodeSelection(selection) ||
|
isNodeSelection(selection) ||
|
||||||
editor.isActive('link') ||
|
editor.isActive('link') ||
|
||||||
editor.isActive('table')
|
editor.isActive('table') ||
|
||||||
|
!editor.isEditable
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,11 @@ const EditorSlots = forwardRef<EditorSlotsRef, EditorSlotsProps>(
|
|||||||
const handleLinkPopup = (
|
const handleLinkPopup = (
|
||||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>
|
e: React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||||
) => {
|
) => {
|
||||||
|
// if editor is not editable, do not show the link popup
|
||||||
|
if (!editor?.isEditable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let popup: Instance<Props>[] = [];
|
let popup: Instance<Props>[] = [];
|
||||||
let component: ReactRenderer;
|
let component: ReactRenderer;
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
|
@ -32,6 +32,9 @@ const mockNodeViewProps = {
|
|||||||
node: mockNode,
|
node: mockNode,
|
||||||
extension: mockExtension,
|
extension: mockExtension,
|
||||||
updateAttributes: mockUpdateAttributes,
|
updateAttributes: mockUpdateAttributes,
|
||||||
|
editor: {
|
||||||
|
isEditable: true,
|
||||||
|
},
|
||||||
} as unknown as NodeViewProps;
|
} as unknown as NodeViewProps;
|
||||||
|
|
||||||
describe('CalloutComponent', () => {
|
describe('CalloutComponent', () => {
|
||||||
@ -70,4 +73,29 @@ describe('CalloutComponent', () => {
|
|||||||
expect(screen.getByTestId('callout-note')).toBeInTheDocument();
|
expect(screen.getByTestId('callout-note')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('callout-danger')).toBeInTheDocument();
|
expect(screen.getByTestId('callout-danger')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not render the popover when callout button is clicked and editor is not editable', async () => {
|
||||||
|
const nodeViewProps = {
|
||||||
|
node: mockNode,
|
||||||
|
extension: mockExtension,
|
||||||
|
updateAttributes: mockUpdateAttributes,
|
||||||
|
editor: {
|
||||||
|
isEditable: false,
|
||||||
|
},
|
||||||
|
} as unknown as NodeViewProps;
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
render(<CalloutComponent {...nodeViewProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
const calloutButton = screen.getByTestId('callout-info-btn');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
userEvent.click(calloutButton);
|
||||||
|
});
|
||||||
|
|
||||||
|
const popover = screen.queryByRole('tooltip');
|
||||||
|
|
||||||
|
expect(popover).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -45,12 +45,18 @@ const CalloutComponent: FC<NodeViewProps> = ({
|
|||||||
node,
|
node,
|
||||||
extension,
|
extension,
|
||||||
updateAttributes,
|
updateAttributes,
|
||||||
|
editor,
|
||||||
}) => {
|
}) => {
|
||||||
const { calloutType } = node.attrs;
|
const { calloutType } = node.attrs;
|
||||||
const [isPopupVisible, setIsPopupVisible] = useState<boolean>(false);
|
const [isPopupVisible, setIsPopupVisible] = useState<boolean>(false);
|
||||||
const CallOutIcon =
|
const CallOutIcon =
|
||||||
CALLOUT_CONTENT[calloutType as keyof typeof CALLOUT_CONTENT];
|
CALLOUT_CONTENT[calloutType as keyof typeof CALLOUT_CONTENT];
|
||||||
|
|
||||||
|
const handlePopoverVisibleChange = (visible: boolean) => {
|
||||||
|
// Only show the popover when the editor is in editable mode
|
||||||
|
setIsPopupVisible(visible && editor.isEditable);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper as="div" className="om-react-node">
|
<NodeViewWrapper as="div" className="om-react-node">
|
||||||
<div
|
<div
|
||||||
@ -73,7 +79,7 @@ const CalloutComponent: FC<NodeViewProps> = ({
|
|||||||
placement="bottomRight"
|
placement="bottomRight"
|
||||||
showArrow={false}
|
showArrow={false}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
onOpenChange={setIsPopupVisible}>
|
onOpenChange={handlePopoverVisibleChange}>
|
||||||
<Button
|
<Button
|
||||||
className="callout-type-btn"
|
className="callout-type-btn"
|
||||||
data-testid={`callout-${calloutType}-btn`}
|
data-testid={`callout-${calloutType}-btn`}
|
||||||
|
@ -25,6 +25,7 @@ import './math-equation.less';
|
|||||||
export const MathEquationComponent: FC<NodeViewProps> = ({
|
export const MathEquationComponent: FC<NodeViewProps> = ({
|
||||||
node,
|
node,
|
||||||
updateAttributes,
|
updateAttributes,
|
||||||
|
editor,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const inputRef = React.useRef<TextAreaRef>(null);
|
const inputRef = React.useRef<TextAreaRef>(null);
|
||||||
@ -78,7 +79,8 @@ export const MathEquationComponent: FC<NodeViewProps> = ({
|
|||||||
) : (
|
) : (
|
||||||
<Latex>{equation}</Latex>
|
<Latex>{equation}</Latex>
|
||||||
)}
|
)}
|
||||||
{!isEditing && (
|
{/* Show edit button only when the editor is editable */}
|
||||||
|
{!isEditing && editor.isEditable && (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
title={t('label.edit-entity', { entity: t('label.equation') })}>
|
title={t('label.edit-entity', { entity: t('label.equation') })}>
|
||||||
<Button
|
<Button
|
||||||
|
@ -26,10 +26,15 @@ const mockNode = {
|
|||||||
const mockDeleteNode = jest.fn();
|
const mockDeleteNode = jest.fn();
|
||||||
const mockUpdateAttributes = jest.fn();
|
const mockUpdateAttributes = jest.fn();
|
||||||
|
|
||||||
|
const mockEditor = {
|
||||||
|
isEditable: true,
|
||||||
|
};
|
||||||
|
|
||||||
const mockNodeViewProps = {
|
const mockNodeViewProps = {
|
||||||
node: mockNode,
|
node: mockNode,
|
||||||
updateAttributes: mockUpdateAttributes,
|
updateAttributes: mockUpdateAttributes,
|
||||||
deleteNode: mockDeleteNode,
|
deleteNode: mockDeleteNode,
|
||||||
|
editor: mockEditor,
|
||||||
} as unknown as NodeViewProps;
|
} as unknown as NodeViewProps;
|
||||||
|
|
||||||
describe('ImageComponent', () => {
|
describe('ImageComponent', () => {
|
||||||
@ -68,6 +73,33 @@ describe('ImageComponent', () => {
|
|||||||
expect(screen.getByText('label.embed-link')).toBeInTheDocument();
|
expect(screen.getByText('label.embed-link')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should not render the popover when image node is clicked and the editor isn't editable", async () => {
|
||||||
|
const nonEditableEditor = {
|
||||||
|
isEditable: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const nonEditableNodeViewProps = {
|
||||||
|
node: mockNode,
|
||||||
|
updateAttributes: mockUpdateAttributes,
|
||||||
|
deleteNode: mockDeleteNode,
|
||||||
|
editor: nonEditableEditor,
|
||||||
|
} as unknown as NodeViewProps;
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
render(<ImageComponent {...nonEditableNodeViewProps} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
const imageNode = screen.getByTestId('uploaded-image-node');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
userEvent.click(imageNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
const popover = screen.queryByRole('tooltip');
|
||||||
|
|
||||||
|
expect(popover).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('should render the upload tab by default', async () => {
|
it('should render the upload tab by default', async () => {
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
render(<ImageComponent {...mockNodeViewProps} />);
|
render(<ImageComponent {...mockNodeViewProps} />);
|
||||||
|
@ -173,6 +173,7 @@ const ImageComponent: FC<NodeViewProps> = ({
|
|||||||
node,
|
node,
|
||||||
updateAttributes,
|
updateAttributes,
|
||||||
deleteNode,
|
deleteNode,
|
||||||
|
editor,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { src, alt } = node.attrs;
|
const { src, alt } = node.attrs;
|
||||||
@ -181,6 +182,11 @@ const ImageComponent: FC<NodeViewProps> = ({
|
|||||||
const [isUploading, setIsUploading] = useState<boolean>(false);
|
const [isUploading, setIsUploading] = useState<boolean>(false);
|
||||||
const [isPopupVisible, setIsPopupVisible] = useState<boolean>(!isValidSource);
|
const [isPopupVisible, setIsPopupVisible] = useState<boolean>(!isValidSource);
|
||||||
|
|
||||||
|
const handlePopoverVisibleChange = (visible: boolean) => {
|
||||||
|
// Only show the popover when the editor is in editable mode
|
||||||
|
setIsPopupVisible(visible && editor.isEditable);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeViewWrapper as="div" className="om-react-node">
|
<NodeViewWrapper as="div" className="om-react-node">
|
||||||
<div className={classNames({ 'om-image-node-wrapper': isPopupVisible })}>
|
<div className={classNames({ 'om-image-node-wrapper': isPopupVisible })}>
|
||||||
@ -203,7 +209,7 @@ const ImageComponent: FC<NodeViewProps> = ({
|
|||||||
placement="bottom"
|
placement="bottom"
|
||||||
showArrow={false}
|
showArrow={false}
|
||||||
trigger="click"
|
trigger="click"
|
||||||
onOpenChange={setIsPopupVisible}>
|
onOpenChange={handlePopoverVisibleChange}>
|
||||||
{isValidSource ? (
|
{isValidSource ? (
|
||||||
<div className="om-image-node-uploaded">
|
<div className="om-image-node-uploaded">
|
||||||
<img
|
<img
|
||||||
|
@ -26,7 +26,7 @@ interface TableMenuProps {
|
|||||||
|
|
||||||
const TableMenu = (props: TableMenuProps) => {
|
const TableMenu = (props: TableMenuProps) => {
|
||||||
const { editor } = props;
|
const { editor } = props;
|
||||||
const { view } = editor;
|
const { view, isEditable } = editor;
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
const menuRef = useRef<HTMLDivElement>(null);
|
||||||
const tableMenuPopup = useRef<Instance | null>(null);
|
const tableMenuPopup = useRef<Instance | null>(null);
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ const TableMenu = (props: TableMenuProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (menuRef.current) {
|
if (menuRef.current && isEditable) {
|
||||||
menuRef.current.remove();
|
menuRef.current.remove();
|
||||||
menuRef.current.style.visibility = 'visible';
|
menuRef.current.style.visibility = 'visible';
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ const TableMenu = (props: TableMenuProps) => {
|
|||||||
tableMenuPopup.current?.destroy();
|
tableMenuPopup.current?.destroy();
|
||||||
tableMenuPopup.current = null;
|
tableMenuPopup.current = null;
|
||||||
};
|
};
|
||||||
}, []);
|
}, [isEditable]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('mousedown', handleMouseDown);
|
document.addEventListener('mousedown', handleMouseDown);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user