mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-15 02:38:42 +00:00
* Fix #4954: UI: Breadcrumb layout breaks with long table names * Fixing code smells Co-authored-by: Vivek Ratnavel Subramanian <vivekratnavel90@gmail.com>
This commit is contained in:
parent
eeab12b0fc
commit
4886d9d4af
@ -25,6 +25,7 @@ import { TITLE_FOR_NON_ADMIN_ACTION } from '../../constants/constants';
|
|||||||
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 { useAuth } from '../../hooks/authHooks';
|
import { useAuth } from '../../hooks/authHooks';
|
||||||
|
import { useAfterMount } from '../../hooks/useAfterMount';
|
||||||
import { ModifiedGlossaryData } from '../../pages/GlossaryPage/GlossaryPageV1.component';
|
import { ModifiedGlossaryData } from '../../pages/GlossaryPage/GlossaryPageV1.component';
|
||||||
import { getEntityDeleteMessage } from '../../utils/CommonUtils';
|
import { getEntityDeleteMessage } from '../../utils/CommonUtils';
|
||||||
import { generateTreeData } from '../../utils/GlossaryUtils';
|
import { generateTreeData } from '../../utils/GlossaryUtils';
|
||||||
@ -110,6 +111,15 @@ const GlossaryV1 = ({
|
|||||||
>([]);
|
>([]);
|
||||||
const [showActions, setShowActions] = useState(false);
|
const [showActions, setShowActions] = useState(false);
|
||||||
const [isDelete, setIsDelete] = useState<boolean>(false);
|
const [isDelete, setIsDelete] = useState<boolean>(false);
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To create breadcrumb from the fqn
|
* To create breadcrumb from the fqn
|
||||||
@ -163,10 +173,23 @@ const GlossaryV1 = ({
|
|||||||
handleBreadcrum(selectedKey);
|
handleBreadcrum(selectedKey);
|
||||||
}, [selectedKey]);
|
}, [selectedKey]);
|
||||||
|
|
||||||
|
useAfterMount(() => {
|
||||||
|
setLeftPanelWidth(
|
||||||
|
document.getElementById('glossary-left-panel')?.offsetWidth || 0
|
||||||
|
);
|
||||||
|
setAddTermButtonWidth(
|
||||||
|
document.getElementById('add-term-button')?.offsetWidth || 0
|
||||||
|
);
|
||||||
|
setManageButtonWidth(
|
||||||
|
document.getElementById('manage-button')?.offsetWidth || 0
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const manageButtonContent = () => {
|
const manageButtonContent = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="tw-flex tw-items-center tw-gap-5 tw-p-1.5 tw-cursor-pointer"
|
className="tw-flex tw-items-center tw-gap-5 tw-p-1.5 tw-cursor-pointer"
|
||||||
|
id="manage-button"
|
||||||
onClick={() => setIsDelete(true)}>
|
onClick={() => setIsDelete(true)}>
|
||||||
<div>
|
<div>
|
||||||
<SVGIcons
|
<SVGIcons
|
||||||
@ -191,7 +214,7 @@ const GlossaryV1 = ({
|
|||||||
|
|
||||||
const fetchLeftPanel = () => {
|
const fetchLeftPanel = () => {
|
||||||
return (
|
return (
|
||||||
<div className="tw-px-2">
|
<div className="tw-px-2" id="glossary-left-panel">
|
||||||
<div className="tw-bg-white tw-shadow-box tw-border tw-border-border-gray tw-rounded-md tw-min-h-full tw-h-80vh tw-py-2">
|
<div className="tw-bg-white tw-shadow-box tw-border tw-border-border-gray tw-rounded-md tw-min-h-full tw-h-80vh tw-py-2">
|
||||||
<div className="tw-flex tw-justify-between tw-items-center tw-px-3">
|
<div className="tw-flex tw-justify-between tw-items-center tw-px-3">
|
||||||
<h6 className="tw-heading tw-text-base">Glossary</h6>
|
<h6 className="tw-heading tw-text-base">Glossary</h6>
|
||||||
@ -256,9 +279,14 @@ const GlossaryV1 = ({
|
|||||||
<div
|
<div
|
||||||
className="tw-heading tw-text-link tw-text-base"
|
className="tw-heading tw-text-link tw-text-base"
|
||||||
data-testid="category-name">
|
data-testid="category-name">
|
||||||
<TitleBreadcrumb titleLinks={breadcrumb} />
|
<TitleBreadcrumb
|
||||||
|
titleLinks={breadcrumb}
|
||||||
|
widthDeductions={
|
||||||
|
leftPanelWidth + addTermButtonWidth + manageButtonWidth + 20 // Additional deduction for margin on the right of leftPanel
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="tw-relative tw-mr-2 tw--mt-2">
|
<div className="tw-relative tw-mr-2 tw--mt-2" id="add-term-button">
|
||||||
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
|
||||||
<Button
|
<Button
|
||||||
className={classNames('tw-h-8 tw-rounded tw-mb-1 tw-mr-2', {
|
className={classNames('tw-h-8 tw-rounded tw-mb-1 tw-mr-2', {
|
||||||
|
@ -23,6 +23,7 @@ import { SettledStatus } from '../../../enums/axios.enum';
|
|||||||
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
|
||||||
import { EntityReference } from '../../../generated/type/entityReference';
|
import { EntityReference } from '../../../generated/type/entityReference';
|
||||||
import { LabelType, State, TagLabel } from '../../../generated/type/tagLabel';
|
import { LabelType, State, TagLabel } from '../../../generated/type/tagLabel';
|
||||||
|
import { useAfterMount } from '../../../hooks/useAfterMount';
|
||||||
import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils';
|
import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils';
|
||||||
import { getEntityFeedLink, getInfoElements } from '../../../utils/EntityUtils';
|
import { getEntityFeedLink, getInfoElements } from '../../../utils/EntityUtils';
|
||||||
import {
|
import {
|
||||||
@ -96,6 +97,9 @@ const EntityPageInfo = ({
|
|||||||
const [tagList, setTagList] = useState<Array<TagOption>>([]);
|
const [tagList, setTagList] = useState<Array<TagOption>>([]);
|
||||||
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
const [tagFetchFailed, setTagFetchFailed] = useState<boolean>(false);
|
||||||
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
const [isTagLoading, setIsTagLoading] = useState<boolean>(false);
|
||||||
|
const [versionFollowButtonWidth, setVersionFollowButtonWidth] = useState(
|
||||||
|
document.getElementById('version-and-follow-section')?.offsetWidth
|
||||||
|
);
|
||||||
|
|
||||||
const handleTagSelection = (selectedTags?: Array<EntityTags>) => {
|
const handleTagSelection = (selectedTags?: Array<EntityTags>) => {
|
||||||
if (selectedTags) {
|
if (selectedTags) {
|
||||||
@ -184,7 +188,7 @@ const EntityPageInfo = ({
|
|||||||
const getVersionButton = (version: string) => {
|
const getVersionButton = (version: string) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="tw-flex tw-h-6 tw-ml-2 tw-mt-2"
|
className="tw-flex tw-h-6 tw-ml-2"
|
||||||
data-testid="version"
|
data-testid="version"
|
||||||
onClick={versionHandler}>
|
onClick={versionHandler}>
|
||||||
<span
|
<span
|
||||||
@ -298,12 +302,23 @@ const EntityPageInfo = ({
|
|||||||
setEntityFollowers(followersList);
|
setEntityFollowers(followersList);
|
||||||
}, [followersList]);
|
}, [followersList]);
|
||||||
|
|
||||||
|
useAfterMount(() => {
|
||||||
|
setVersionFollowButtonWidth(
|
||||||
|
document.getElementById('version-and-follow-section')?.offsetWidth
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="entity-page-info">
|
<div data-testid="entity-page-info">
|
||||||
<div className="tw-flex tw-flex-col">
|
<div className="tw-flex tw-flex-col">
|
||||||
<div className="tw-flex tw-flex-initial tw-justify-between tw-items-start">
|
<div className="tw-flex tw-flex-initial tw-justify-between tw-items-start">
|
||||||
<div className="tw-flex tw-items-center">
|
<div className="tw-flex tw-items-center">
|
||||||
<TitleBreadcrumb titleLinks={titleLinks} />
|
<TitleBreadcrumb
|
||||||
|
titleLinks={titleLinks}
|
||||||
|
widthDeductions={
|
||||||
|
(versionFollowButtonWidth ? versionFollowButtonWidth : 0) + 30
|
||||||
|
}
|
||||||
|
/>
|
||||||
{deleted && (
|
{deleted && (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
@ -318,7 +333,9 @@ const EntityPageInfo = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="tw-flex tw-py-1">
|
<div
|
||||||
|
className="tw-flex tw-py-1 tw-mt-1"
|
||||||
|
id="version-and-follow-section">
|
||||||
{!isUndefined(version) ? (
|
{!isUndefined(version) ? (
|
||||||
<>
|
<>
|
||||||
{!isUndefined(isVersionSelected) ? (
|
{!isUndefined(isVersionSelected) ? (
|
||||||
@ -339,7 +356,7 @@ const EntityPageInfo = ({
|
|||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{!isUndefined(isFollowing) ? (
|
{!isUndefined(isFollowing) ? (
|
||||||
<div className="tw-flex tw-h-6 tw-ml-2 tw-mt-2">
|
<div className="tw-flex tw-h-6 tw-ml-2">
|
||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'tw-flex tw-border tw-border-primary tw-rounded',
|
'tw-flex tw-border tw-border-primary tw-rounded',
|
||||||
|
@ -14,7 +14,13 @@
|
|||||||
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
|
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { FunctionComponent } from 'react';
|
import React, {
|
||||||
|
FunctionComponent,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { TitleBreadcrumbProps } from './title-breadcrumb.interface';
|
import { TitleBreadcrumbProps } from './title-breadcrumb.interface';
|
||||||
|
|
||||||
@ -22,16 +28,48 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
|
|||||||
titleLinks,
|
titleLinks,
|
||||||
className = '',
|
className = '',
|
||||||
noLink = false,
|
noLink = false,
|
||||||
|
widthDeductions,
|
||||||
}: TitleBreadcrumbProps) => {
|
}: TitleBreadcrumbProps) => {
|
||||||
|
const [screenWidth, setScreenWidth] = useState(window.innerWidth);
|
||||||
|
|
||||||
|
const finalWidthOfBreadcrumb = useMemo(() => {
|
||||||
|
return (
|
||||||
|
screenWidth -
|
||||||
|
(widthDeductions ? widthDeductions : 0) - // Any extra deductions due to sibling elements of breadcrumb
|
||||||
|
(titleLinks.length - 1) * 25 - // Deduction for every arrow between each titleLink name
|
||||||
|
80 // Deduction due to margin of the container on both sides
|
||||||
|
);
|
||||||
|
}, [screenWidth, titleLinks, widthDeductions]);
|
||||||
|
|
||||||
|
const maxWidth = useMemo(() => {
|
||||||
|
return finalWidthOfBreadcrumb / titleLinks.length;
|
||||||
|
}, [finalWidthOfBreadcrumb, titleLinks.length]);
|
||||||
|
|
||||||
|
const changeWidth = useCallback(() => {
|
||||||
|
setScreenWidth(window.innerWidth);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener('resize', changeWidth);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', changeWidth);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className={className} data-testid="breadcrumb">
|
<nav className={className} data-testid="breadcrumb">
|
||||||
<ol className="list-reset tw-py-2 tw-rounded tw-flex">
|
<ol className="list-reset tw-py-2 tw-rounded tw-flex">
|
||||||
{titleLinks.map((link, index) => {
|
{titleLinks.map((link, index) => {
|
||||||
const classes =
|
const classes =
|
||||||
'link-title' + (link.activeTitle ? ' tw-font-medium' : '');
|
'link-title tw-truncate' +
|
||||||
|
(link.activeTitle ? ' tw-font-medium' : '');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li data-testid="breadcrumb-link" key={index}>
|
<li
|
||||||
|
className="tw-flex tw-items-center"
|
||||||
|
data-testid="breadcrumb-link"
|
||||||
|
key={index}>
|
||||||
{link.imgSrc ? (
|
{link.imgSrc ? (
|
||||||
<img
|
<img
|
||||||
alt=""
|
alt=""
|
||||||
@ -41,7 +79,12 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
|
|||||||
) : null}
|
) : null}
|
||||||
{index < titleLinks.length - 1 && !noLink ? (
|
{index < titleLinks.length - 1 && !noLink ? (
|
||||||
<>
|
<>
|
||||||
<Link className={classes} to={link.url}>
|
<Link
|
||||||
|
className={classes}
|
||||||
|
style={{
|
||||||
|
maxWidth,
|
||||||
|
}}
|
||||||
|
to={link.url}>
|
||||||
{link.name}
|
{link.name}
|
||||||
</Link>
|
</Link>
|
||||||
<span className="tw-px-2">
|
<span className="tw-px-2">
|
||||||
@ -52,7 +95,12 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
) : link.url ? (
|
) : link.url ? (
|
||||||
<Link className={classes} to={link.url}>
|
<Link
|
||||||
|
className={classes}
|
||||||
|
style={{
|
||||||
|
maxWidth,
|
||||||
|
}}
|
||||||
|
to={link.url}>
|
||||||
{link.name}
|
{link.name}
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
@ -62,7 +110,10 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
|
|||||||
classes,
|
classes,
|
||||||
'tw-cursor-text hover:tw-text-primary hover:tw-no-underline'
|
'tw-cursor-text hover:tw-text-primary hover:tw-no-underline'
|
||||||
)}
|
)}
|
||||||
data-testid="inactive-link">
|
data-testid="inactive-link"
|
||||||
|
style={{
|
||||||
|
maxWidth,
|
||||||
|
}}>
|
||||||
{link.name}
|
{link.name}
|
||||||
</span>
|
</span>
|
||||||
{noLink && index < titleLinks.length - 1 && (
|
{noLink && index < titleLinks.length - 1 && (
|
||||||
|
@ -20,4 +20,5 @@ export type TitleBreadcrumbProps = {
|
|||||||
}>;
|
}>;
|
||||||
className?: string;
|
className?: string;
|
||||||
noLink?: boolean;
|
noLink?: boolean;
|
||||||
|
widthDeductions?: number;
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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 { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export const useAfterMount = (fnCallback: () => void) => {
|
||||||
|
const [isMounting, setIsMounting] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isMounting) {
|
||||||
|
fnCallback();
|
||||||
|
}
|
||||||
|
}, [isMounting]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsMounting(false);
|
||||||
|
}, []);
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user