Fix #4954: UI: Breadcrumb layout breaks with long table names (#5385)

* 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:
darth-coder00 2022-06-10 00:50:43 +05:30 committed by GitHub
parent eeab12b0fc
commit 4886d9d4af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 138 additions and 13 deletions

View File

@ -25,6 +25,7 @@ import { TITLE_FOR_NON_ADMIN_ACTION } from '../../constants/constants';
import { Glossary } from '../../generated/entity/data/glossary';
import { GlossaryTerm } from '../../generated/entity/data/glossaryTerm';
import { useAuth } from '../../hooks/authHooks';
import { useAfterMount } from '../../hooks/useAfterMount';
import { ModifiedGlossaryData } from '../../pages/GlossaryPage/GlossaryPageV1.component';
import { getEntityDeleteMessage } from '../../utils/CommonUtils';
import { generateTreeData } from '../../utils/GlossaryUtils';
@ -110,6 +111,15 @@ const GlossaryV1 = ({
>([]);
const [showActions, setShowActions] = useState(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
@ -163,10 +173,23 @@ const GlossaryV1 = ({
handleBreadcrum(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 = () => {
return (
<div
className="tw-flex tw-items-center tw-gap-5 tw-p-1.5 tw-cursor-pointer"
id="manage-button"
onClick={() => setIsDelete(true)}>
<div>
<SVGIcons
@ -191,7 +214,7 @@ const GlossaryV1 = ({
const fetchLeftPanel = () => {
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-flex tw-justify-between tw-items-center tw-px-3">
<h6 className="tw-heading tw-text-base">Glossary</h6>
@ -256,9 +279,14 @@ const GlossaryV1 = ({
<div
className="tw-heading tw-text-link tw-text-base"
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 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}>
<Button
className={classNames('tw-h-8 tw-rounded tw-mb-1 tw-mr-2', {

View File

@ -23,6 +23,7 @@ import { SettledStatus } from '../../../enums/axios.enum';
import { Operation } from '../../../generated/entity/policies/accessControl/rule';
import { EntityReference } from '../../../generated/type/entityReference';
import { LabelType, State, TagLabel } from '../../../generated/type/tagLabel';
import { useAfterMount } from '../../../hooks/useAfterMount';
import { getHtmlForNonAdminAction } from '../../../utils/CommonUtils';
import { getEntityFeedLink, getInfoElements } from '../../../utils/EntityUtils';
import {
@ -96,6 +97,9 @@ const EntityPageInfo = ({
const [tagList, setTagList] = useState<Array<TagOption>>([]);
const [tagFetchFailed, setTagFetchFailed] = 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>) => {
if (selectedTags) {
@ -184,7 +188,7 @@ const EntityPageInfo = ({
const getVersionButton = (version: string) => {
return (
<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"
onClick={versionHandler}>
<span
@ -298,12 +302,23 @@ const EntityPageInfo = ({
setEntityFollowers(followersList);
}, [followersList]);
useAfterMount(() => {
setVersionFollowButtonWidth(
document.getElementById('version-and-follow-section')?.offsetWidth
);
});
return (
<div data-testid="entity-page-info">
<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-items-center">
<TitleBreadcrumb titleLinks={titleLinks} />
<TitleBreadcrumb
titleLinks={titleLinks}
widthDeductions={
(versionFollowButtonWidth ? versionFollowButtonWidth : 0) + 30
}
/>
{deleted && (
<>
<div
@ -318,7 +333,9 @@ const EntityPageInfo = ({
</>
)}
</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(isVersionSelected) ? (
@ -339,7 +356,7 @@ const EntityPageInfo = ({
</>
) : null}
{!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
className={classNames(
'tw-flex tw-border tw-border-primary tw-rounded',

View File

@ -14,7 +14,13 @@
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
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 { TitleBreadcrumbProps } from './title-breadcrumb.interface';
@ -22,16 +28,48 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
titleLinks,
className = '',
noLink = false,
widthDeductions,
}: 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 (
<nav className={className} data-testid="breadcrumb">
<ol className="list-reset tw-py-2 tw-rounded tw-flex">
{titleLinks.map((link, index) => {
const classes =
'link-title' + (link.activeTitle ? ' tw-font-medium' : '');
'link-title tw-truncate' +
(link.activeTitle ? ' tw-font-medium' : '');
return (
<li data-testid="breadcrumb-link" key={index}>
<li
className="tw-flex tw-items-center"
data-testid="breadcrumb-link"
key={index}>
{link.imgSrc ? (
<img
alt=""
@ -41,7 +79,12 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
) : null}
{index < titleLinks.length - 1 && !noLink ? (
<>
<Link className={classes} to={link.url}>
<Link
className={classes}
style={{
maxWidth,
}}
to={link.url}>
{link.name}
</Link>
<span className="tw-px-2">
@ -52,7 +95,12 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
</span>
</>
) : link.url ? (
<Link className={classes} to={link.url}>
<Link
className={classes}
style={{
maxWidth,
}}
to={link.url}>
{link.name}
</Link>
) : (
@ -62,7 +110,10 @@ const TitleBreadcrumb: FunctionComponent<TitleBreadcrumbProps> = ({
classes,
'tw-cursor-text hover:tw-text-primary hover:tw-no-underline'
)}
data-testid="inactive-link">
data-testid="inactive-link"
style={{
maxWidth,
}}>
{link.name}
</span>
{noLink && index < titleLinks.length - 1 && (

View File

@ -20,4 +20,5 @@ export type TitleBreadcrumbProps = {
}>;
className?: string;
noLink?: boolean;
widthDeductions?: number;
};

View File

@ -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);
}, []);
};