This is an automated email from the ASF dual-hosted git repository. pawarprasad123 pushed a commit to branch atlas-2.6 in repository https://gitbox.apache.org/repos/asf/atlas.git
commit 0fef6d7fb755e1be45856db2e0de8697c15d551d Author: Brijesh Bhalala <[email protected]> AuthorDate: Thu Jul 16 19:43:38 2026 +0530 ATLAS-5330: Atlas React UI: Text overflow issues in Global Search, Re… (#683) ( cherry-picked from commit 400db52af8fe3029057a926b47c00f09e4084145) * ATLAS-5330: Atlas React UI: Text overflow issues in Global Search, Recent Activity, Search Filters, and Popovers for long classification names * ATLAS-5330: Atlas React UI: Text overflow issues in Global Search, Recent Activity, Search Filters, and Popovers for long classification names * ATLAS-5330: Atlas React UI: Text overflow issues in Global Search, Recent Activity, Search Filters, and Popovers for long classification names --- dashboard/src/components/DialogShowMoreLess.tsx | 10 +- dashboard/src/components/FilterQuery.tsx | 67 ++++--- .../src/components/Forms/FormAutocomplete.tsx | 4 +- .../src/components/Forms/FormCreatableSelect.tsx | 4 +- dashboard/src/components/Forms/FormDatepicker.tsx | 4 +- dashboard/src/components/Forms/FormInputText.tsx | 4 +- .../src/components/Forms/FormSelectBoolean.tsx | 4 +- .../src/components/Forms/FormSingleSelect.tsx | 4 +- dashboard/src/components/Forms/FormTextArea.tsx | 4 +- .../src/components/GlobalSearch/QuickSearch.tsx | 185 +++++++++--------- dashboard/src/components/Modal.tsx | 34 ++-- .../QueryBuilder/RelationshipFilters.tsx | 8 +- .../__tests__/RelationshipFilters.test.tsx | 10 +- .../TagFilters/TagCustomValueEditor.tsx | 33 +++- .../QueryBuilder/TagFilters/TagFilters.tsx | 8 +- .../TagFilters/__tests__/TagFilters.test.tsx | 10 +- .../TypeFilters/TypeCustomValueEditor.tsx | 88 +++++++-- .../QueryBuilder/TypeFilters/TypeFilters.tsx | 4 +- .../__tests__/RelationshipFilters.test.tsx | 2 +- .../src/components/ShowMore/DrawerBodyChipView.tsx | 88 +++++---- dashboard/src/components/ShowMore/ShowMoreView.tsx | 28 ++- .../src/components/TypeDefAuditDetailModal.tsx | 53 ++---- dashboard/src/components/commonComponents.tsx | 39 ++-- dashboard/src/index.scss | 117 +++++++++++- dashboard/src/styles/filterQuery.scss | 6 + dashboard/src/styles/filterQueryBuilder.scss | 3 + dashboard/src/styles/stats.scss | 4 + .../views/Administrator/Audits/AuditResults.tsx | 48 ++++- .../Administrator/Audits/ImportExportAudits.tsx | 26 +-- dashboard/src/views/Classification/AddTag.tsx | 108 +++++++---- .../views/Classification/ClassificationForm.tsx | 209 ++++++++++++--------- .../DashboardOverview/ClassificationCoverage.tsx | 5 + .../src/views/DashboardOverview/RecentActivity.tsx | 18 +- .../src/views/DetailPage/DetailPageAttributes.tsx | 34 ++-- .../EntityDetailTabs/AuditTableDetails.tsx | 15 +- .../EntityDetailTabs/ClassificationsTab.tsx | 114 +++++++---- .../src/views/SideBar/Import/ImportLayout.tsx | 5 +- .../src/views/Statistics/ClassificationStats.tsx | 4 +- 38 files changed, 910 insertions(+), 501 deletions(-) diff --git a/dashboard/src/components/DialogShowMoreLess.tsx b/dashboard/src/components/DialogShowMoreLess.tsx index ebe35f9f2..2e7ab3a23 100644 --- a/dashboard/src/components/DialogShowMoreLess.tsx +++ b/dashboard/src/components/DialogShowMoreLess.tsx @@ -608,8 +608,14 @@ const DialogShowMoreLess = ({ </Typography> ) : ( <Typography fontSize={15}> - Remove: <b>{currentValue.selectedValue}</b> assignment from{" "} - <b>{currentValue.assetName}</b> ? + Remove:{" "} + <b + title={currentValue.selectedValue} + className="text-truncate-inline" + > + {currentValue.selectedValue} + </b>{" "} + assignment from <b>{currentValue.assetName}</b> ? </Typography> )} </CustomModal> diff --git a/dashboard/src/components/FilterQuery.tsx b/dashboard/src/components/FilterQuery.tsx index 20351c33a..057f37340 100644 --- a/dashboard/src/components/FilterQuery.tsx +++ b/dashboard/src/components/FilterQuery.tsx @@ -74,16 +74,16 @@ export const FilterQuery = ({ value }: any) => { data-cy={`${obj.id}${key}`} label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey"> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}> {systemAttributes[obj.id] ? systemAttributes[obj.id] : obj.id} </Typography> - <Typography className="operator" fontWeight="600"> + <Typography className="operator" fontWeight="600" sx={{ flexShrink: 0 }}> {getDisplayOperator(obj.operator)}{" "} </Typography> - <Typography className="searchValue"> + <Typography className="searchValue" title={filterQueryValue[obj.id] ? filterQueryValue[obj.id][obj.value] : obj.value}> {filterQueryValue[obj.id] ? filterQueryValue[obj.id][obj.value] : obj.value} @@ -97,6 +97,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); } @@ -281,11 +282,11 @@ export const FilterQuery = ({ value }: any) => { data-type="type" label={ <> - <Stack direction="row"> - <Typography className="searchKey" gap="0.125em"> + <Stack direction="row" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" gap="0.125em" sx={{ flexShrink: 0 }}> Type: </Typography> - <Typography className="searchValue">{value.type}</Typography> + <Typography className="searchValue" title={value.type}>{value.type}</Typography> </Stack> </> } @@ -295,6 +296,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); let entityFilters: any = attributeFilter.extractUrl({ @@ -341,9 +343,9 @@ export const FilterQuery = ({ value }: any) => { data-type="tag" label={ <> - <Stack direction="row" gap="0.125em" alignItems="center"> - <Typography className="searchKey">Classification: </Typography> - <Typography className="searchValue">{value.tag}</Typography> + <Stack direction="row" gap="0.125em" alignItems="center" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}>Classification: </Typography> + <Typography className="searchValue" title={value.tag} noWrap>{value.tag}</Typography> </Stack> </> } @@ -353,6 +355,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); let tagFilters: any = attributeFilter.extractUrl({ @@ -397,9 +400,9 @@ export const FilterQuery = ({ value }: any) => { data-type="relationshipName" label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey">Relationship: </Typography> - <Typography className="searchValue"> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}>Relationship: </Typography> + <Typography className="searchValue" title={value.relationshipName}> {value.relationshipName} </Typography> </Stack> @@ -411,6 +414,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); let relationshipFilters: any = attributeFilter.extractUrl({ @@ -421,7 +425,7 @@ export const FilterQuery = ({ value }: any) => { if (value.relationshipFilters) { const conditionForRelationship: any = value.relationshipFilters.rules && - value?.relationshipFilters?.rules?.length === 1 + value?.relationshipFilters?.rules?.length === 1 ? "" : "AND"; relationshipKeyValue = ( @@ -461,9 +465,9 @@ export const FilterQuery = ({ value }: any) => { data-type="term" label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey">Term: </Typography> - <Typography className="searchValue">{value.term}</Typography> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}>Term: </Typography> + <Typography className="searchValue" title={value.term}>{value.term}</Typography> </Stack> </> } @@ -473,6 +477,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); @@ -488,9 +493,9 @@ export const FilterQuery = ({ value }: any) => { data-type="query" label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey">Query: </Typography> - <Typography className="searchValue"> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}>Query: </Typography> + <Typography className="searchValue" title={value.query.trim()}> {value.query.trim()} </Typography> </Stack> @@ -502,6 +507,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); @@ -517,9 +523,9 @@ export const FilterQuery = ({ value }: any) => { data-type="excludeST" label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey">Exclude sub-types: </Typography> - <Typography className="searchValue">{value.excludeST}</Typography> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}>Exclude sub-types: </Typography> + <Typography className="searchValue" title={value.excludeST}>{value.excludeST}</Typography> </Stack> </> } @@ -529,6 +535,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); @@ -544,11 +551,11 @@ export const FilterQuery = ({ value }: any) => { data-type="excludeSC" label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey"> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}> Exclude sub-classifications:{" "} </Typography> - <Typography className="searchValue">{value.excludeSC}</Typography> + <Typography className="searchValue" title={value.excludeSC}>{value.excludeSC}</Typography> </Stack> </> } @@ -558,6 +565,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); @@ -573,11 +581,11 @@ export const FilterQuery = ({ value }: any) => { data-type="includeDE" label={ <> - <Stack direction="row" gap="0.125em"> - <Typography className="searchKey"> + <Stack direction="row" gap="0.125em" sx={{ maxWidth: '100%', overflow: 'hidden' }}> + <Typography className="searchKey" sx={{ flexShrink: 0 }}> Show historical entities:{" "} </Typography> - <Typography className="searchValue">{value.includeDE}</Typography> + <Typography className="searchValue" title={value.includeDE}>{value.includeDE}</Typography> </Stack> </> } @@ -587,6 +595,7 @@ export const FilterQuery = ({ value }: any) => { size="small" variant="outlined" clickable + sx={{ maxWidth: '100%', '& .MuiChip-label': { overflow: 'hidden', display: 'flex', alignItems: 'center' } }} /> ); diff --git a/dashboard/src/components/Forms/FormAutocomplete.tsx b/dashboard/src/components/Forms/FormAutocomplete.tsx index 188b1c867..c1735c2ef 100644 --- a/dashboard/src/components/Forms/FormAutocomplete.tsx +++ b/dashboard/src/components/Forms/FormAutocomplete.tsx @@ -110,11 +110,11 @@ const FormAutocomplete = ({ data, control }: any) => { <LightTooltip title={`Data Type: (${typeName})`}> <Typography color="#666666" - textOverflow="ellipsis" + maxWidth="160px" overflow="hidden" fontSize={14} - >{`(${typeName})`}</Typography> + noWrap>{`(${typeName})`}</Typography> </LightTooltip> </div> <Autocomplete diff --git a/dashboard/src/components/Forms/FormCreatableSelect.tsx b/dashboard/src/components/Forms/FormCreatableSelect.tsx index bd53b0a85..549cb9898 100644 --- a/dashboard/src/components/Forms/FormCreatableSelect.tsx +++ b/dashboard/src/components/Forms/FormCreatableSelect.tsx @@ -48,11 +48,11 @@ const FormCreatableSelect = ({ data, control, fieldName }: any) => { <LightTooltip title={`Data Type: (${typeName})`}> <Typography color="#666666" - textOverflow="ellipsis" + maxWidth="160px" overflow="hidden" fontSize={14} - >{`(${typeName}) ${cardinality}`}</Typography> + noWrap>{`(${typeName}) ${cardinality}`}</Typography> </LightTooltip> </div> <Autocomplete diff --git a/dashboard/src/components/Forms/FormDatepicker.tsx b/dashboard/src/components/Forms/FormDatepicker.tsx index eae6882c2..44cb37f69 100644 --- a/dashboard/src/components/Forms/FormDatepicker.tsx +++ b/dashboard/src/components/Forms/FormDatepicker.tsx @@ -64,10 +64,10 @@ const FormDatepicker = ({ data, control, fieldName }: any) => { <Typography color="#666666" overflow="hidden" - textOverflow="ellipsis" + maxWidth="160px" fontSize={14} - >{`(${typeName})`}</Typography> + noWrap>{`(${typeName})`}</Typography> </LightTooltip> </div> <div className="w-100"> diff --git a/dashboard/src/components/Forms/FormInputText.tsx b/dashboard/src/components/Forms/FormInputText.tsx index 73b4cc2c2..582a04f01 100644 --- a/dashboard/src/components/Forms/FormInputText.tsx +++ b/dashboard/src/components/Forms/FormInputText.tsx @@ -45,10 +45,10 @@ const FormInputText = ({ <Typography color="#666666" overflow="hidden" - textOverflow="ellipsis" + maxWidth="160px" fontSize={14} - >{`(${typeName})`}</Typography> + noWrap>{`(${typeName})`}</Typography> </LightTooltip> </div> <TextField diff --git a/dashboard/src/components/Forms/FormSelectBoolean.tsx b/dashboard/src/components/Forms/FormSelectBoolean.tsx index 95284b787..c93849460 100644 --- a/dashboard/src/components/Forms/FormSelectBoolean.tsx +++ b/dashboard/src/components/Forms/FormSelectBoolean.tsx @@ -47,11 +47,11 @@ const FormSelectBoolean = ({ data, control, fieldName }: any) => { <LightTooltip title={`Data Type: (${typeName})`}> <Typography color="#666666" - textOverflow="ellipsis" + overflow="hidden" maxWidth="160px" fontSize={14} - >{`(${typeName})`}</Typography> + noWrap>{`(${typeName})`}</Typography> </LightTooltip> </div> <div style={{ width: "100%" }}> diff --git a/dashboard/src/components/Forms/FormSingleSelect.tsx b/dashboard/src/components/Forms/FormSingleSelect.tsx index a55fd18e4..97cf6ba72 100644 --- a/dashboard/src/components/Forms/FormSingleSelect.tsx +++ b/dashboard/src/components/Forms/FormSingleSelect.tsx @@ -52,11 +52,11 @@ const FormSingleSelect = ({ <Typography color="#666666" - textOverflow="ellipsis" + overflow="hidden" maxWidth="160px" fontSize={14} - >{`(${typeName})`}</Typography> + noWrap>{`(${typeName})`}</Typography> </div> <div style={{ width: "100%" }} className="form-textfield"> <Select diff --git a/dashboard/src/components/Forms/FormTextArea.tsx b/dashboard/src/components/Forms/FormTextArea.tsx index 7a5d33411..ee848f7bd 100644 --- a/dashboard/src/components/Forms/FormTextArea.tsx +++ b/dashboard/src/components/Forms/FormTextArea.tsx @@ -42,11 +42,11 @@ const FormTextArea = ({ data, control }: any) => { <LightTooltip title={`Data Type: (${typeName})`}> <Typography color="#666666" - textOverflow="ellipsis" + maxWidth="160px" overflow="hidden" fontSize={14} - >{`(${typeName})`}</Typography> + noWrap>{`(${typeName})`}</Typography> </LightTooltip> </div> <div className="w-100"> diff --git a/dashboard/src/components/GlobalSearch/QuickSearch.tsx b/dashboard/src/components/GlobalSearch/QuickSearch.tsx index 58452964b..f3f860f78 100644 --- a/dashboard/src/components/GlobalSearch/QuickSearch.tsx +++ b/dashboard/src/components/GlobalSearch/QuickSearch.tsx @@ -198,24 +198,24 @@ const QuickSearch = () => { entities = !isEmpty(searchResults?.entities) ? searchResults?.entities?.map((entityDef: any) => { - const { name }: { name: string; found: boolean; key: any } = - extractKeyValueFromEntity(entityDef); - return { - title: `${name}`, - parent: entityDef.typeName, - types: "Entities", - entityObj: entityDef - }; - }) + const { name }: { name: string; found: boolean; key: any } = + extractKeyValueFromEntity(entityDef); + return { + title: `${name}`, + parent: entityDef.typeName, + types: "Entities", + entityObj: entityDef + }; + }) : [{ title: "No Entities Found", types: "Entities" }]; suggestionNames = !isEmpty(suggestions) ? suggestions.map((suggestion: any) => { - return { - title: `${suggestion}`, - types: "Suggestions" - }; - }) + return { + title: `${suggestion}`, + types: "Suggestions" + }; + }) : [{ title: "No Suggestions Found", types: "Suggestions" }]; setOptions([...entities, ...suggestionNames] as GlobalOptionRow[]); @@ -439,6 +439,9 @@ const QuickSearch = () => { sx={{ minWidth: 150, flex: "1 1 280px", + "& .MuiAutocomplete-popper": { + maxWidth: "100%" + }, "& + .MuiAutocomplete-popper .MuiAutocomplete-option": { backgroundColor: "white" }, @@ -446,6 +449,14 @@ const QuickSearch = () => { backgroundColor: "#c7e3ff" } }} + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} value={null} onChange={(_event: unknown, newValue: unknown) => { if (newValue && typeof newValue === "object") { @@ -476,13 +487,19 @@ const QuickSearch = () => { const parts = parse(title, matches); return ( <li {...props} key={row.scoped.id}> - <Typography component="span" variant="body2"> + <Typography + component="span" + variant="body2" + sx={{ + display: "block", + width: "100%", + }} + title={title} + noWrap> {parts.map((part, index) => ( <span key={index} - style={{ - fontWeight: part.highlight ? 700 : 400 - }} + className={part.highlight ? "fw-bold" : "fw-normal"} > {part.text} </span> @@ -494,14 +511,14 @@ const QuickSearch = () => { const { entityObj, types, parent } = typeof option !== "string" && - "entityObj" in option && - "types" in option && - "parent" in option + "entityObj" in option && + "types" in option && + "parent" in option ? (option as { - entityObj: { status?: string; guid?: string }; - types: string; - parent: string; - }) + entityObj: { status?: string; guid?: string }; + types: string; + parent: string; + }) : { entityObj: null, types: "", parent: "" }; const title = typeof option !== "string" && "title" in option @@ -528,36 +545,31 @@ const QuickSearch = () => { ); return ( <Stack - flexDirection="row" component="li" className="global-search-options" + direction="row" + alignItems="center" sx={{ "& > span": { mr: 2, flexShrink: 0 - } + }, + overflow: "hidden", + width: "100%" }} {...props} + title={safeTitle} onClick={() => { handleValues(option as GlobalOptionRow); }} > {types === "Entities" && !isEmpty(entityObj) ? ( <Link - className="entity-name text-decoration-none" - style={{ - maxWidth: "100%", - width: "100%", - color: "black", - textDecoration: "none", - display: "inline-flex", - alignItems: "center", - flexWrap: "wrap" - }} + className="entity-name text-decoration-none text-black-no-decoration text-truncate-block" to={{ pathname: href }} color={ entityObj?.status && - entityStateReadOnly[entityObj.status] + entityStateReadOnly[entityObj.status] ? "error" : "primary" } @@ -567,65 +579,58 @@ const QuickSearch = () => { )} {types === "Entities" && !isEmpty(entityObj) ? parts.map((part, index) => ( - <Stack - flexDirection="row" - key={index} - style={{ - fontWeight: part.highlight ? "bold" : "regular" - }} - > - {entityObj?.guid !== "-1" && !part.highlight ? ( - <Link - className="entity-name text-blue text-decoration-none" - style={{ - color: "black", - textDecoration: "none", - maxWidth: "100%", - width: "100%" - }} - to={{ pathname: href }} - color={ - entityObj?.status && + <span + key={index} + className={part.highlight ? "fw-bold" : "fw-normal"} + > + {entityObj?.guid !== "-1" && !part.highlight ? ( + <Link + className="entity-name text-blue text-black-no-decoration max-100 w-100" + to={{ pathname: href }} + color={ + entityObj?.status && entityStateReadOnly[entityObj.status] - ? "error" - : "primary" - } - > - {part.text} - </Link> - ) : ( - part.text - )} - </Stack> - )) + ? "error" + : "primary" + } + > + {part.text} + </Link> + ) : ( + part.text + )} + </span> + )) : parts.map((part, index) => ( - <Stack - flexDirection="row" - key={index} - style={{ - fontWeight: part.highlight ? "bold" : "regular" - }} - > - {part.text} - </Stack> - ))} + <span + key={index} + className={part.highlight ? "fw-bold" : "fw-normal"} + > + {part.text} + </span> + ))} {types === "Entities" && !isEmpty(entityObj) && ` (${parent})`} </Link> ) : ( - parts.map((part, index) => ( - <Typography - component="p" - key={index} - className="global-search-options-text" - sx={{ - fontWeight: part.highlight ? "bold" : "regular" - }} - > - {part.text} - </Typography> - )) + <Typography + component="span" + className="global-search-options-text" + sx={{ + display: "block", + width: "100%", + }} + noWrap> + {parts.map((part, index) => ( + <span + key={index} + className={part.highlight ? "fw-bold" : "fw-normal"} + > + {part.text} + </span> + ))} + </Typography> )} </Stack> ); diff --git a/dashboard/src/components/Modal.tsx b/dashboard/src/components/Modal.tsx index 373e5fee2..e91b16d85 100644 --- a/dashboard/src/components/Modal.tsx +++ b/dashboard/src/components/Modal.tsx @@ -24,7 +24,7 @@ import DialogTitle from "@mui/material/DialogTitle"; import IconButton from "@mui/material/IconButton"; import Stack from "@mui/material/Stack"; import Typography from "@mui/material/Typography"; -import { CloseIcon, CustomButton } from "./muiComponents"; +import { CloseIcon, CustomButton, LightTooltip } from "./muiComponents"; import { isEmpty } from "../utils/Utils"; const BootstrapDialog = styled(Dialog)(() => ({ @@ -118,14 +118,26 @@ export const CustomModal: React.FC<CustomModalProps> = ({ sx={{ padding: "0", display: "flex", - alignItems: "center" + alignItems: "center", + flex: 1, + minWidth: 0 }} > - <Stack direction="row" alignItems="center" gap="0.5rem"> + <Stack direction="row" alignItems="center" gap="0.5rem" sx={{ flex: 1, minWidth: 0 }}> {titleIcon != undefined && titleIcon} - <Typography className={"modal-title"} sx={{ color: "#333" }}> - {title} - </Typography> + <LightTooltip title={title}> + <Typography + className={"modal-title"} + sx={{ + color: "#333", + display: "block", + flex: 1, + minWidth: 0 + }} + noWrap> + {title} + </Typography> + </LightTooltip> {postTitleIcon != undefined && postTitleIcon} </Stack> </DialogTitle> @@ -201,11 +213,11 @@ export const CustomModal: React.FC<CustomModalProps> = ({ }), ...(primaryDisabled && !isLoading && { - "&.Mui-disabled": { - pointerEvents: "unset", - cursor: "not-allowed" - } - }) + "&.Mui-disabled": { + pointerEvents: "unset", + cursor: "not-allowed" + } + }) }} startIcon={ isLoading ? ( diff --git a/dashboard/src/components/QueryBuilder/RelationshipFilters.tsx b/dashboard/src/components/QueryBuilder/RelationshipFilters.tsx index a9937aa1c..3dd0aaebc 100644 --- a/dashboard/src/components/QueryBuilder/RelationshipFilters.tsx +++ b/dashboard/src/components/QueryBuilder/RelationshipFilters.tsx @@ -48,8 +48,8 @@ const RelationshipFilters = ({ let attrTagObj = !isEmpty(relationshipDefs) && !isEmpty(relationshipParams) ? relationshipDefs.find((relationship: { name: string }) => { - return relationship.name == relationshipParams; - }) + return relationship.name == relationshipParams; + }) : {}; if (attrTagObj) { @@ -107,8 +107,8 @@ const RelationshipFilters = ({ return ( <Accordion defaultExpanded> - <AccordionSummary aria-controls="panel1-content" id="panel1-header"> - <Typography className="text-color-green" fontWeight="600"> + <AccordionSummary aria-controls="panel1-content" id="panel1-header" sx={{ '& .MuiAccordionSummary-content': { overflow: 'hidden' } }}> + <Typography className="text-color-green w-100" fontWeight="600" noWrap title={`Relationship: ${relationshipParams}`}> Relationship: {relationshipParams} </Typography> </AccordionSummary> diff --git a/dashboard/src/components/QueryBuilder/RelationshipFilters/__tests__/RelationshipFilters.test.tsx b/dashboard/src/components/QueryBuilder/RelationshipFilters/__tests__/RelationshipFilters.test.tsx index f04e37749..d2c9010e7 100644 --- a/dashboard/src/components/QueryBuilder/RelationshipFilters/__tests__/RelationshipFilters.test.tsx +++ b/dashboard/src/components/QueryBuilder/RelationshipFilters/__tests__/RelationshipFilters.test.tsx @@ -79,7 +79,7 @@ jest.mock('react-querybuilder', () => { } }) } - + return React.createElement('div', { 'data-testid': 'query-builder' }, React.createElement('div', { 'data-testid': 'fields-count' }, fields?.length || 0), React.createElement('div', { 'data-testid': 'query' }, JSON.stringify(query)), @@ -123,9 +123,9 @@ jest.mock('@components/muiComponents', () => ({ <div data-testid="accordion-details">{children}</div> ), Typography: ({ children, className, fontWeight, textAlign, color }: any) => ( - <span - className={className} - style={{ fontWeight, textAlign }} + <span + className={className} + style={{ fontWeight, textAlign }} data-color={color} > {children} @@ -267,7 +267,7 @@ describe('RelationshipFilters', () => { ) const typography = screen.getByText(/Relationship:/) - expect(typography.className).toBe('text-color-green') + expect(typography.className).toContain('text-color-green') expect(typography.style.fontWeight).toBe('600') }) }) diff --git a/dashboard/src/components/QueryBuilder/TagFilters/TagCustomValueEditor.tsx b/dashboard/src/components/QueryBuilder/TagFilters/TagCustomValueEditor.tsx index 8ea45809d..123fe31d1 100644 --- a/dashboard/src/components/QueryBuilder/TagFilters/TagCustomValueEditor.tsx +++ b/dashboard/src/components/QueryBuilder/TagFilters/TagCustomValueEditor.tsx @@ -57,10 +57,10 @@ export const TagCustomValueEditor = (props: ValueEditorProps) => { let tagOptions = !isEmpty(tagData) ? tagData - .map((obj: { name: any }) => { - return obj.name; - }) - .sort() + .map((obj: { name: any }) => { + return obj.name; + }) + .sort() : []; if (props.operator === "is_null" || props.operator === "not_null") { @@ -78,11 +78,36 @@ export const TagCustomValueEditor = (props: ValueEditorProps) => { disableClearable clearOnEscape={false} size="small" + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} + renderOption={(props, option) => ( + <li {...props} title={option} className={props.className}> + <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", display: "block", width: "100%" }}> + {option} + </span> + </li> + )} renderInput={(params) => ( <TextField {...params} variant="outlined" size="small" + inputProps={{ + ...params.inputProps, + title: selectedTypenameValue, + style: { + ...(params.inputProps?.style ?? {}), + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden" + } + }} sx={{ background: "white" }} diff --git a/dashboard/src/components/QueryBuilder/TagFilters/TagFilters.tsx b/dashboard/src/components/QueryBuilder/TagFilters/TagFilters.tsx index 6df48d05e..88df1cd70 100644 --- a/dashboard/src/components/QueryBuilder/TagFilters/TagFilters.tsx +++ b/dashboard/src/components/QueryBuilder/TagFilters/TagFilters.tsx @@ -57,8 +57,8 @@ const TagFilters = ({ let attrTagObj = !isEmpty(classificationDefs) && !isEmpty(tag) ? classificationDefs.find((tag: { name: string }) => { - return tag.name == tagParams; - }) + return tag.name == tagParams; + }) : {}; if (attrTagObj) { @@ -163,8 +163,8 @@ const TagFilters = ({ defaultExpanded sx={{ borderBottom: "1px solid rgba(0, 0, 0, 0.12) !important" }} > - <AccordionSummary aria-controls="panel1-content" id="panel1-header"> - <Typography className="text-color-green" fontWeight="600"> + <AccordionSummary aria-controls="panel1-content" id="panel1-header" sx={{ '& .MuiAccordionSummary-content': { overflow: 'hidden' } }}> + <Typography className="text-color-green w-100" fontWeight="600" noWrap title={`Classification: ${tagParams}`}> Classification: {tagParams} </Typography> </AccordionSummary> diff --git a/dashboard/src/components/QueryBuilder/TagFilters/__tests__/TagFilters.test.tsx b/dashboard/src/components/QueryBuilder/TagFilters/__tests__/TagFilters.test.tsx index d4f195f41..af70707d0 100644 --- a/dashboard/src/components/QueryBuilder/TagFilters/__tests__/TagFilters.test.tsx +++ b/dashboard/src/components/QueryBuilder/TagFilters/__tests__/TagFilters.test.tsx @@ -123,10 +123,10 @@ describe('TagFilters', () => { beforeEach(() => { jest.clearAllMocks() - + // Reset location mock mockUseLocation.mockReturnValue({ search: '?tag=PII&type=DataSet' }) - + // Set up useAppSelector mock implementation mockUseAppSelector.mockImplementation((selector: any) => { const state = createMockState() @@ -138,7 +138,7 @@ describe('TagFilters', () => { } return result }) - + isEmpty.mockImplementation( (val: any) => !val || (Array.isArray(val) && val.length === 0) ) @@ -153,7 +153,9 @@ describe('TagFilters', () => { /> ) - expect(screen.getByText(/Classification:.*PII/)).toBeTruthy() + const textNode = screen.getByText(/Classification:.*PII/) + expect(textNode).toBeTruthy() + expect(textNode.className).toContain('w-100') }) it('renders QueryBuilder when fields are available', () => { diff --git a/dashboard/src/components/QueryBuilder/TypeFilters/TypeCustomValueEditor.tsx b/dashboard/src/components/QueryBuilder/TypeFilters/TypeCustomValueEditor.tsx index 64a7a1cd7..6e361b09e 100644 --- a/dashboard/src/components/QueryBuilder/TypeFilters/TypeCustomValueEditor.tsx +++ b/dashboard/src/components/QueryBuilder/TypeFilters/TypeCustomValueEditor.tsx @@ -65,26 +65,26 @@ export const TypeCustomValueEditor = (props: ValueEditorProps) => { let tagOptions = !isEmpty(classificationDefs) ? classificationDefs - .map((obj: { name: any }) => { - return obj.name; - }) - .sort() + .map((obj: { name: any }) => { + return obj.name; + }) + .sort() : []; let typeData = !isEmpty(typeHeaderData) ? typeHeaderData.filter((obj: { category: string }) => { - if (obj.category == "ENTITY") { - return obj; - } - }) + if (obj.category == "ENTITY") { + return obj; + } + }) : []; let entityOptions = !isEmpty(typeData) ? typeData - .map((obj: { name: any }) => { - return obj.name; - }) - .sort() + .map((obj: { name: any }) => { + return obj.name; + }) + .sort() : []; if (props.operator === "is_null" || props.operator === "not_null") { @@ -103,8 +103,38 @@ export const TypeCustomValueEditor = (props: ValueEditorProps) => { disableClearable clearOnEscape={false} size="small" + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} + renderOption={(props, option) => ( + <li {...props} title={option} className={props.className}> + <span className="autocomplete-option-label"> + {option} + </span> + </li> + )} renderInput={(params) => ( - <TextField {...params} variant="outlined" size="small" fullWidth /> + <TextField + {...params} + variant="outlined" + size="small" + fullWidth + inputProps={{ + ...params.inputProps, + title: selectedValue, + style: { + ...(params.inputProps?.style ?? {}), + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden" + } + }} + /> )} sx={{ minWidth: "200px" }} /> @@ -121,8 +151,38 @@ export const TypeCustomValueEditor = (props: ValueEditorProps) => { disableClearable clearOnEscape={false} size="small" + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} + renderOption={(props, option) => ( + <li {...props} title={option} className={props.className}> + <span className="autocomplete-option-label"> + {option} + </span> + </li> + )} renderInput={(params) => ( - <TextField {...params} variant="outlined" size="small" fullWidth /> + <TextField + {...params} + variant="outlined" + size="small" + fullWidth + inputProps={{ + ...params.inputProps, + title: selectedTypenameValue, + style: { + ...(params.inputProps?.style ?? {}), + textOverflow: "ellipsis", + whiteSpace: "nowrap", + overflow: "hidden" + } + }} + /> )} sx={{ minWidth: "200px" }} /> diff --git a/dashboard/src/components/QueryBuilder/TypeFilters/TypeFilters.tsx b/dashboard/src/components/QueryBuilder/TypeFilters/TypeFilters.tsx index cc047695d..3cb4d2f11 100644 --- a/dashboard/src/components/QueryBuilder/TypeFilters/TypeFilters.tsx +++ b/dashboard/src/components/QueryBuilder/TypeFilters/TypeFilters.tsx @@ -36,11 +36,13 @@ const TypeFilters = ({ fieldsObj, typeQuery, setTypeQuery }: any) => { defaultExpanded sx={{ borderBottom: "1px solid rgba(0, 0, 0, 0.12) !important" }} > - <AccordionSummary aria-controls="panel1-content" id="panel1-header"> + <AccordionSummary aria-controls="panel1-content" id="panel1-header" sx={{ '& .MuiAccordionSummary-content': { overflow: 'hidden' } }}> <Typography className="text-color-green" fontSize="16px" fontWeight="600" + noWrap sx={{ width: "100%" }} + title={`Type: ${typeParams}`} > Type: {typeParams} </Typography> diff --git a/dashboard/src/components/QueryBuilder/__tests__/RelationshipFilters.test.tsx b/dashboard/src/components/QueryBuilder/__tests__/RelationshipFilters.test.tsx index a89952827..c19ff002f 100644 --- a/dashboard/src/components/QueryBuilder/__tests__/RelationshipFilters.test.tsx +++ b/dashboard/src/components/QueryBuilder/__tests__/RelationshipFilters.test.tsx @@ -277,7 +277,7 @@ describe('RelationshipFilters', () => { ) const typography = screen.getByText(/Relationship:/) - expect(typography.className).toBe('text-color-green') + expect(typography.className).toContain('text-color-green') expect(typography.style.fontWeight).toBe('600') }) }) diff --git a/dashboard/src/components/ShowMore/DrawerBodyChipView.tsx b/dashboard/src/components/ShowMore/DrawerBodyChipView.tsx index 38f702af6..8d6c06c6f 100644 --- a/dashboard/src/components/ShowMore/DrawerBodyChipView.tsx +++ b/dashboard/src/components/ShowMore/DrawerBodyChipView.tsx @@ -100,10 +100,10 @@ const DrawerBodyChipView = ({ const filteredData = !isEmpty(data) ? data.filter((tag: any) => { - return (tag[displayKey] || tag) - ?.toLowerCase() - ?.includes(searchTerm?.toLowerCase()); - }) + return (tag[displayKey] || tag) + ?.toLowerCase() + ?.includes(searchTerm?.toLowerCase()); + }) : []; const handleRemove = async () => { @@ -114,18 +114,18 @@ const DrawerBodyChipView = ({ } else if (title == "Terms" || title == "Category") { let selectedTerm: any = !isEmpty(data) ? data?.find((obj: any) => { - const typedObj = obj as { - qualifiedName: string; - displayText: string; - termGuid: string; - }; - if ( - (typedObj.qualifiedName || typedObj.displayText) == - currentValue.selectedValue - ) { - return typedObj; - } - }) + const typedObj = obj as { + qualifiedName: string; + displayText: string; + termGuid: string; + }; + if ( + (typedObj.qualifiedName || typedObj.displayText) == + currentValue.selectedValue + ) { + return typedObj; + } + }) : {}; if (isEmpty(gType)) { @@ -185,8 +185,8 @@ const DrawerBodyChipView = ({ const checkSuperTypes = (classificationName: string) => { var tagObj = !isEmpty(classificationData.classificationDefs) ? classificationData.classificationDefs.find((obj: { name: string }) => { - return obj.name == classificationName; - }) + return obj.name == classificationName; + }) : {}; return !isEmpty(tagObj?.superTypes) @@ -198,10 +198,10 @@ const DrawerBodyChipView = ({ const getTagParentList = (name: string) => { let tagObj = !isEmpty(classificationDefs) - ? classificationDefs?.find((obj: { name: string }) => { - return obj.name == name; - }) - : {}, + ? classificationDefs?.find((obj: { name: string }) => { + return obj.name == name; + }) + : {}, tagParents = tagObj ? tagObj?.["superTypes"] : null, parentName = name; if (tagParents && tagParents.length) { @@ -330,10 +330,10 @@ const DrawerBodyChipView = ({ onDelete={ !isEmpty(removeApiMethod) && !isDeleteIcon ? () => { - handleDelete(obj[displayKey] || obj); - } + handleDelete(obj[displayKey] || obj); + } : isDeleteIcon && obj.count > 1 - ? () => { + ? () => { const searchParams = new URLSearchParams(); searchParams.set("tabActive", "classification"); searchParams.set("filter", obj.typeName); @@ -342,7 +342,7 @@ const DrawerBodyChipView = ({ search: searchParams.toString() }); } - : undefined + : undefined } deleteIcon={ isDeleteIcon && obj.count > 1 ? ( @@ -394,23 +394,37 @@ const DrawerBodyChipView = ({ Remove:{" "} <Typography component="span" - display="inline" - sx={{ fontWeight: 600 }} - > + title={currentValue.selectedValue} + sx={{ + fontWeight: 600, + display: "inline-block", + maxWidth: "100%", + verticalAlign: "bottom" + }} + noWrap> {currentValue.selectedValue} </Typography>{" "} assignment from{" "} <Typography component="span" - display="inline" - sx={{ fontWeight: 600 }} - > + title={!isEmpty(currentEntity) + ? `${currentValue.assetName} ${!isEmpty(currentEntity?.typeName) + ? `(${currentEntity.typeName})` + : "" + }` + : ""} + sx={{ + fontWeight: 600, + display: "inline-block", + maxWidth: "100%", + verticalAlign: "bottom" + }} + noWrap> {!isEmpty(currentEntity) - ? `${currentValue.assetName} ${ - !isEmpty(currentEntity?.typeName) - ? `(${currentEntity.typeName})` - : "" - }` + ? `${currentValue.assetName} ${!isEmpty(currentEntity?.typeName) + ? `(${currentEntity.typeName})` + : "" + }` : ""} </Typography>{" "} ? diff --git a/dashboard/src/components/ShowMore/ShowMoreView.tsx b/dashboard/src/components/ShowMore/ShowMoreView.tsx index 48f368d0c..d6543410e 100644 --- a/dashboard/src/components/ShowMore/ShowMoreView.tsx +++ b/dashboard/src/components/ShowMore/ShowMoreView.tsx @@ -395,17 +395,33 @@ const ShowMoreView = ({ Remove:{" "} <Typography component="span" - display="inline" - sx={{ fontWeight: 600 }} - > + title={currentValue.selectedValue} + sx={{ + fontWeight: 600, + display: "inline-block", + maxWidth: "100%", + verticalAlign: "bottom" + }} + noWrap> {currentValue.selectedValue} </Typography>{" "} assignment from{" "} <Typography component="span" - display="inline" - sx={{ fontWeight: 600 }} - > + title={!isEmpty(currentEntity) + ? `${currentValue.assetName} ${ + !isEmpty(currentEntity?.typeName) + ? `(${currentEntity.typeName})` + : "" + }` + : ""} + sx={{ + fontWeight: 600, + display: "inline-block", + maxWidth: "100%", + verticalAlign: "bottom" + }} + noWrap> {!isEmpty(currentEntity) ? `${currentValue.assetName} ${ !isEmpty(currentEntity?.typeName) diff --git a/dashboard/src/components/TypeDefAuditDetailModal.tsx b/dashboard/src/components/TypeDefAuditDetailModal.tsx index 1e780d6fb..a992eacb6 100644 --- a/dashboard/src/components/TypeDefAuditDetailModal.tsx +++ b/dashboard/src/components/TypeDefAuditDetailModal.tsx @@ -66,39 +66,26 @@ export const TypeDefAuditDetailModal = ({ <StyledPaper variant="outlined"> {!isEmpty(detailObject) ? Object.entries(detailObject as Record<string, unknown>) - .sort(([a], [b]) => a.localeCompare(b)) - .map(([keys, value]) => ( - <div key={keys}> - <Stack direction="row" spacing={4} marginBottom={1} marginTop={1}> - <div - style={{ - flex: 1, - wordBreak: "break-all", - textAlign: "left", - fontWeight: "600", - }} - > - {`${keys} ${isArray(value) ? `(${(value as unknown[]).length})` : ""}`} - </div> - <div - style={{ - flex: 1, - wordBreak: "break-all", - textAlign: "left", - }} - > - {getValues( - value, - undefined, - undefined, - undefined, - "properties" - )} - </div> - </Stack> - <Divider /> - </div> - )) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([keys, value]) => ( + <div key={keys}> + <Stack direction="row" spacing={4} marginBottom={1} marginTop={1}> + <div className="text-truncate-flex text-left fw-600"> + {`${keys} ${isArray(value) ? `(${(value as unknown[]).length})` : ""}`} + </div> + <div className="text-truncate-flex text-left"> + {getValues( + value, + undefined, + undefined, + undefined, + "properties" + )} + </div> + </Stack> + <Divider /> + </div> + )) : "No Record Found"} </StyledPaper> </CustomModal> diff --git a/dashboard/src/components/commonComponents.tsx b/dashboard/src/components/commonComponents.tsx index 449a26bb1..5d320d895 100644 --- a/dashboard/src/components/commonComponents.tsx +++ b/dashboard/src/components/commonComponents.tsx @@ -196,10 +196,15 @@ export const ExtractObject = (props: any) => { search: searchParams.toString() ? searchParams.toString() : "" }} style={{ - width: "unset !important", - whiteSpace: "normal", + display: "inline-block", + maxWidth: "100%", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + verticalAlign: "bottom", color: deleteIcon ? "#bb5838" : "#4a90e2" }} + title={nameVal} > {nameVal} </Link> @@ -299,15 +304,15 @@ export const getValues = ( relationShipAttr != undefined ? entityData : properties !== undefined - ? values - : values?.row?.original?.attributes[entity?.name] + ? values + : values?.row?.original?.attributes[entity?.name] } }; let keyValue = tempObj.valueObject[entity?.name]; let currentValue = properties !== undefined ? values : values.getValue(); let attributeTypeName: string = !isEmpty(entity?.attributeDefs) ? entity?.attributeDefs?.find((obj: { name: string }) => obj.name == keys) - ?.typeName + ?.typeName : ""; // Filter out profileData, but show isIncomplete (matching Classic UI behavior) @@ -329,10 +334,10 @@ export const getValues = ( <span> {moment(keyValue).isValid() ? formatedDate({ - date: keyValue - // zone: false, - // dateFormat: dateFormat - }) + date: keyValue + // zone: false, + // dateFormat: dateFormat + }) : "N/A"} </span> ) @@ -352,8 +357,8 @@ export const getValues = ( filterEntityData?.relationshipAttributes?.[keys as string] != undefined ? filterEntityData.relationshipAttributes[keys as string] : properties !== undefined - ? values - : values.getValue(); + ? values + : values.getValue(); return ( <GetArrayValue @@ -379,8 +384,8 @@ export const getValues = ( filterEntityData?.relationshipAttributes?.[keys as string] != undefined ? filterEntityData.relationshipAttributes[keys as string] : properties !== undefined - ? values - : values.getValue(); + ? values + : values.getValue(); return ( <GetArrayValue values={filteredValues} @@ -430,10 +435,10 @@ export const getValues = ( <span> {moment(keyValue).isValid() ? formatedDate({ - date: currentValue, - zone: false, - dateFormat: dateFormat - }) + date: currentValue, + zone: false, + dateFormat: dateFormat + }) : "N/A"} </span> ) diff --git a/dashboard/src/index.scss b/dashboard/src/index.scss index efef5940e..a05d9fbec 100644 --- a/dashboard/src/index.scss +++ b/dashboard/src/index.scss @@ -43,6 +43,7 @@ font-style: normal; font-display: swap; } + @font-face { font-family: "Source Sans 3"; src: url("/fonts/source-sans-3/SourceSans3-Medium.woff2") format("woff2"); @@ -50,6 +51,7 @@ font-style: normal; font-display: swap; } + @font-face { font-family: "Source Sans 3"; src: url("/fonts/source-sans-3/SourceSans3-SemiBold.woff2") format("woff2"); @@ -110,12 +112,15 @@ root { ::-webkit-scrollbar-corner { background: #000; } + .text-grey { color: #686868; } + .cursor-pointer { cursor: pointer !important; } + .cursor-not-allowed { cursor: not-allowed !important; } @@ -123,12 +128,12 @@ root { .text-color-green { color: #37bb9b; } -.text-grey { - color: #686868; -} + + .text-blue { color: #1976d2; } + .text-red { color: #bb5838 !important; } @@ -185,6 +190,7 @@ root { .p-0 { padding: 0 !important; } + .pb-1 { padding-bottom: 1rem !important; } @@ -197,6 +203,7 @@ root { .flex-1 { flex: 1 !important; } + .text-center { text-align: center !important; } @@ -211,6 +218,7 @@ root { background-color: #ffffff; } } + textarea { resize: vertical; } @@ -230,3 +238,106 @@ textarea { textarea::placeholder { opacity: 0.7; } + +.Toastify__toast-body { + word-break: break-all; +} + +.text-truncate { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + display: block !important; + min-width: 0 !important; +} + +.text-truncate-flex { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + flex: 1 1 0% !important; + min-width: 0 !important; +} + +.MuiTypography-noWrap { + min-width: 0 !important; +} + +.w-100 { + width: 100% !important; +} + +.text-left { + text-align: left !important; +} + +.fw-600 { + font-weight: 600 !important; +} + +.recent-activity-truncate { + display: inline-block !important; + max-width: 300px !important; + vertical-align: bottom !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.text-truncate-block { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-truncate-inline { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + vertical-align: bottom; +} + +.flex-center-gap { + display: flex; + align-items: center; + gap: 0.5rem; + width: 100%; +} + +.text-black-no-decoration { + color: black !important; + text-decoration: none !important; +} + +.max-100 { + max-width: 100% !important; +} + +.fw-normal { + font-weight: normal !important; +} + +.fw-bold { + font-weight: 700 !important; +} + +.autocomplete-option-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 100%; +} + +.min-w-0 { + min-width: 0 !important; +} \ No newline at end of file diff --git a/dashboard/src/styles/filterQuery.scss b/dashboard/src/styles/filterQuery.scss index 8acfa7785..f181982b2 100644 --- a/dashboard/src/styles/filterQuery.scss +++ b/dashboard/src/styles/filterQuery.scss @@ -25,4 +25,10 @@ .searchValue { color: v.$tag_color; font-weight: 600 !important; + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + vertical-align: bottom; } diff --git a/dashboard/src/styles/filterQueryBuilder.scss b/dashboard/src/styles/filterQueryBuilder.scss index 584919cc6..b03486e7f 100644 --- a/dashboard/src/styles/filterQueryBuilder.scss +++ b/dashboard/src/styles/filterQueryBuilder.scss @@ -75,6 +75,9 @@ border-radius: 4px; color: #555; line-height: 1.42857143; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } .rule-remove, diff --git a/dashboard/src/styles/stats.scss b/dashboard/src/styles/stats.scss index 854a2981e..03f8037fd 100644 --- a/dashboard/src/styles/stats.scss +++ b/dashboard/src/styles/stats.scss @@ -56,3 +56,7 @@ font-weight: bold !important; color: rgba(0, 0, 0, 0.87) !important; } + +.classification-name-cell { + max-width: 400px; +} diff --git a/dashboard/src/views/Administrator/Audits/AuditResults.tsx b/dashboard/src/views/Administrator/Audits/AuditResults.tsx index a287456f7..984221b15 100644 --- a/dashboard/src/views/Administrator/Audits/AuditResults.tsx +++ b/dashboard/src/views/Administrator/Audits/AuditResults.tsx @@ -51,10 +51,10 @@ const AuditResults = ({ componentProps, row }: any) => { return ( <> {operation != "PURGE" && - operation != "AUTO_PURGE" && - operation != "IMPORT" && - operation != "EXPORT" && - !isEmpty(resultObj) ? ( + operation != "AUTO_PURGE" && + operation != "IMPORT" && + operation != "EXPORT" && + !isEmpty(resultObj) ? ( <Grid container spacing={2}> {params.split(",").length > 1 ? ( <> @@ -70,9 +70,8 @@ const AuditResults = ({ componentProps, row }: any) => { > <Typography sx={{ padding: "1rem 0 0 1rem", textAlign: "left" }} - >{`${category[param as any]} ${ - auditAction[operation] - }`}</Typography> + >{`${category[param as any]} ${auditAction[operation] + }`}</Typography> <List className="audit-results-list"> {resultObj[param as any].map( @@ -89,6 +88,16 @@ const AuditResults = ({ componentProps, row }: any) => { setOpenModal(true); setCurrentObj(obj); }} + title={name} + sx={{ + display: "inline-block", + maxWidth: "100%", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + textAlign: "left", + verticalAlign: "bottom" + }} > {name} </Link> @@ -115,9 +124,8 @@ const AuditResults = ({ componentProps, row }: any) => { > <Typography sx={{ padding: "1rem 0 0 1rem", textAlign: "left" }} - >{`${category[params as any]} ${ - auditAction[operation] - }`}</Typography> + >{`${category[params as any]} ${auditAction[operation] + }`}</Typography> <List className="audit-results-list"> {resultObj[params].map((obj: { name: string }) => { const { name } = obj; @@ -132,6 +140,16 @@ const AuditResults = ({ componentProps, row }: any) => { setOpenModal(true); setCurrentObj(obj); }} + title={name} + sx={{ + display: "inline-block", + maxWidth: "100%", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + textAlign: "left", + verticalAlign: "bottom" + }} > {name} </Link> @@ -169,6 +187,16 @@ const AuditResults = ({ componentProps, row }: any) => { setOpenPurgeModal(true); setCurrentPurgeResultObj(obj); }} + title={obj} + sx={{ + display: "inline-block", + maxWidth: "100%", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + textAlign: "left", + verticalAlign: "bottom" + }} > {obj} </Link> diff --git a/dashboard/src/views/Administrator/Audits/ImportExportAudits.tsx b/dashboard/src/views/Administrator/Audits/ImportExportAudits.tsx index 92a5a682d..b2df73b67 100644 --- a/dashboard/src/views/Administrator/Audits/ImportExportAudits.tsx +++ b/dashboard/src/views/Administrator/Audits/ImportExportAudits.tsx @@ -45,23 +45,14 @@ const ImportExportAudits = ({ auditObj }: any) => { marginTop={1} > <div - style={{ - flex: 1, - wordBreak: "break-all", - textAlign: "left", - fontWeight: "600" - }} + className="text-truncate-flex text-left fw-600" > {`${keys} ${ isArray(value) ? `(${value.length})` : "" }`} </div> <div - style={{ - flex: 1, - wordBreak: "break-all", - textAlign: "left" - }} + className="text-truncate-flex text-left" > {getValues( value, @@ -94,23 +85,14 @@ const ImportExportAudits = ({ auditObj }: any) => { marginTop={1} > <div - style={{ - flex: 1, - wordBreak: "break-all", - textAlign: "left", - fontWeight: "600" - }} + className="text-truncate-flex text-left fw-600" > {`${keys} ${ isArray(value) ? `(${value.length})` : "" }`} </div> <div - style={{ - flex: 1, - wordBreak: "break-all", - textAlign: "left" - }} + className="text-truncate-flex text-left" > {getValues( paramsObj, diff --git a/dashboard/src/views/Classification/AddTag.tsx b/dashboard/src/views/Classification/AddTag.tsx index 1ec08ac9f..8d4a8921d 100644 --- a/dashboard/src/views/Classification/AddTag.tsx +++ b/dashboard/src/views/Classification/AddTag.tsx @@ -137,8 +137,8 @@ const AddTag = (props: { const classificationNames = !isEmpty(classifications) ? classifications.map((obj: { typeName: any }) => { - return obj.typeName; - }) + return obj.typeName; + }) : []; const nonAssignTag = classificationDefs.filter((obj: { name: string }) => { @@ -147,23 +147,23 @@ const AddTag = (props: { const options = !isEmpty(nonAssignTag) ? nonAssignTag.map((obj: { name: any }) => ({ - label: obj.name, - value: obj.name - })) + label: obj.name, + value: obj.name + })) : []; const classificationObj = !isEmpty(tagName) ? classificationDefs.find((obj: { name: string }) => { - return obj.name == (isAdd ? tagName.label : tagName); - }) + return obj.name == (isAdd ? tagName.label : tagName); + }) : null; let attributeDefList = !isEmpty(classificationObj) ? getNestedSuperTypeObj({ - data: classificationObj, - collection: classificationDefs, - attrMerge: true - }) + data: classificationObj, + collection: classificationDefs, + attrMerge: true + }) : null; const renderFormControl: any = (obj: any) => { @@ -287,8 +287,8 @@ const AddTag = (props: { if (isAdd) { let bulkGuids = isArray(entityData) ? entityData.map((obj: { guid: any }) => { - return obj.guid; - }) + return obj.guid; + }) : [entityGuid]; data["entityGuids"] = bulkGuids; @@ -386,8 +386,8 @@ const AddTag = (props: { isEmpty(classificationData) ? false : isAdd - ? !isEmpty(tagName) - : true + ? !isEmpty(tagName) + : true } > <form onSubmit={handleSubmit(onSubmit)}> @@ -400,9 +400,9 @@ const AddTag = (props: { isAdd ? null : { - label: entityData.typeName, - value: entityData.typeName - } + label: entityData.typeName, + value: entityData.typeName + } } render={({ field: { onChange, value } }) => { return ( @@ -411,6 +411,9 @@ const AddTag = (props: { size="small" onChange={(_event, item) => { onChange(item); + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur(); + } }} value={value} disabled={isAdd ? false : true} @@ -421,6 +424,27 @@ const AddTag = (props: { } options={customSortBy(options, ["label"])} className="form-autocomplete-field" + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} + renderOption={(props, option: any) => { + return ( + <li + {...props} + title={option.label} + className={props.className} + > + <span className="autocomplete-option-label"> + {option.label} + </span> + </li> + ); + }} renderInput={(params) => ( <TextField {...params} @@ -429,7 +453,15 @@ const AddTag = (props: { InputProps={{ ...params.InputProps }} + sx={{ + "& .MuiAutocomplete-input": { + textOverflow: "ellipsis !important", + overflow: "hidden !important", + whiteSpace: "nowrap !important" + } + }} placeholder={`Search Classification`} + title={value?.label || ""} /> )} /> @@ -472,24 +504,24 @@ const AddTag = (props: { /> {(checkModalTagProperty == undefined || checkModalTagProperty) && ( - <Controller - control={control} - name={"removePropagationsOnEntityDelete"} - data-cy="removePropagationsOnEntityDelete" - render={({ field: { onChange, value } }) => ( - <FormControlLabel - control={ - <Checkbox - size="small" - checked={value} - onChange={onChange} - /> - } - label=" Remove propagation on entity delete" - /> - )} - /> - )}{" "} + <Controller + control={control} + name={"removePropagationsOnEntityDelete"} + data-cy="removePropagationsOnEntityDelete" + render={({ field: { onChange, value } }) => ( + <FormControlLabel + control={ + <Checkbox + size="small" + checked={value} + onChange={onChange} + /> + } + label=" Remove propagation on entity delete" + /> + )} + /> + )}{" "} </Stack> </Card> <Card @@ -518,8 +550,8 @@ const AddTag = (props: { </Stack> {(checkTimezoneProperty || entityData?.validityPeriods?.length > 0) && ( - <AddValidityPeriod control={control} /> - )} + <AddValidityPeriod control={control} /> + )} {!isEmpty(attributeDefList) && ( <div> <Typography fontWeight={600} sx={{ paddingBottom: "1rem" }}> diff --git a/dashboard/src/views/Classification/ClassificationForm.tsx b/dashboard/src/views/Classification/ClassificationForm.tsx index ff02028d9..7eb60e10e 100644 --- a/dashboard/src/views/Classification/ClassificationForm.tsx +++ b/dashboard/src/views/Classification/ClassificationForm.tsx @@ -22,6 +22,7 @@ import { CustomButton, LightTooltip } from "@components/muiComponents"; import { useAppDispatch, useAppSelector } from "@hooks/reducerHook"; import { Autocomplete, + Chip, CircularProgress, IconButton, InputLabel, @@ -70,8 +71,8 @@ const ClassificationForm = ({ const { enumDefs } = enumObj.data; const classificationObj = !isEmpty(currentTagName) ? classificationData.classificationDefs.find( - (obj: { name: string }) => obj.name == currentTagName - ) + (obj: { name: string }) => obj.name == currentTagName + ) : {}; const { name, description } = classificationObj; @@ -127,9 +128,9 @@ const ClassificationForm = ({ const options = !isEmpty(classificationData) ? classificationData.classificationDefs.map((obj: { name: any }) => ({ - label: obj.name, - value: obj.name - })) + label: obj.name, + value: obj.name + })) : []; const enumType = !isEmpty(enumDefs) @@ -200,8 +201,7 @@ const ClassificationForm = ({ onClose(); toast.dismiss(toastId.current); toastId.current = toast.success( - `Classification ${name} was ${ - isAdd ? "created" : "updated" + `Classification ${name} was ${isAdd ? "created" : "updated" } successfully` ); await fetchInitialData(); @@ -385,6 +385,38 @@ const ClassificationForm = ({ } options={options} className="form-autocomplete-field" + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} + renderOption={(props, option: any) => { + return ( + <li + {...props} + title={option.label} + className={props.className} + > + <span className="autocomplete-option-label"> + {option.label} + </span> + </li> + ); + }} + renderTags={(tagValue, getTagProps) => + tagValue.map((option, index) => ( + <Chip + {...getTagProps({ index })} + key={index} + label={option.label} + title={option.label} + size="small" + /> + )) + } renderInput={(params) => ( <TextField {...params} @@ -432,99 +464,94 @@ const ClassificationForm = ({ watched?.[index] && watched?.[index]?.typeName == "array<string>"; return ( - <Stack gap="1rem" key={field.id} direction="row"> - <TextField - margin="normal" - fullWidth - {...register(`attributes.${index}.attributeName`)} - defaultValue={field.attributeName} - variant="outlined" - size="small" - placeholder={"Attribute Name"} - className="form-textfield" - sx={{ - marginTop: "8px !important", - marginBottom: "8px !important" - }} - /> - - <div - style={{ - width: "100%", - display: "flex", - alignItems: "center", - gap: "0.5rem" - }} - > - <Select + <Stack gap="1rem" key={field.id} direction="row"> + <TextField + margin="normal" fullWidth + {...register(`attributes.${index}.attributeName`)} + defaultValue={field.attributeName} + variant="outlined" size="small" - defaultValue={"string"} - id="demo-select-small" - {...register(`attributes.${index}.typeName`)} + placeholder={"Attribute Name"} className="form-textfield" sx={{ marginTop: "8px !important", marginBottom: "8px !important" }} + /> + + <div + className="flex-center-gap" > - {dataTypeSelector.map((type) => ( - <MenuItem key={type} value={type}> - {type} - </MenuItem> - ))} - </Select> - {shouldShowToggle && ( - <Controller - control={control} - name={ - `attributes.${index}.toggleDuplicates` as const - } - data-cy={`attributes.${index}.toggleDuplicates`} - defaultValue={field.toggleDuplicates} - render={({ field: { value, onChange } }: any) => ( - <> - <LightTooltip - title={ - value == false ? "Make LIST" : "Make SET" - } - > - <AntSwitch - size="small" - {...register( - `attributes.${index}.toggleDuplicates` - )} - checked={value} - onChange={onChange} - sx={{ marginRight: "4px" }} - inputProps={{ - "aria-label": "controlled" - }} - /> - </LightTooltip> - </> - )} - /> - )} - </div> + <Select + fullWidth + size="small" + defaultValue={"string"} + id="demo-select-small" + {...register(`attributes.${index}.typeName`)} + className="form-textfield" + sx={{ + marginTop: "8px !important", + marginBottom: "8px !important" + }} + > + {dataTypeSelector.map((type) => ( + <MenuItem key={type} value={type}> + {type} + </MenuItem> + ))} + </Select> + {shouldShowToggle && ( + <Controller + control={control} + name={ + `attributes.${index}.toggleDuplicates` as const + } + data-cy={`attributes.${index}.toggleDuplicates`} + defaultValue={field.toggleDuplicates} + render={({ field: { value, onChange } }: any) => ( + <> + <LightTooltip + title={ + value == false ? "Make LIST" : "Make SET" + } + > + <AntSwitch + size="small" + {...register( + `attributes.${index}.toggleDuplicates` + )} + checked={value} + onChange={onChange} + sx={{ marginRight: "4px" }} + inputProps={{ + "aria-label": "controlled" + }} + /> + </LightTooltip> + </> + )} + /> + )} + </div> - <IconButton - aria-label="back" - color="error" - sx={{ - display: "inline-flex", - position: "relative", - padding: "4px", - marginLeft: "4px" - }} - onClick={(e) => { - e.stopPropagation(); - remove(index); - }} - > - <ClearOutlinedIcon sx={{ fontSize: "1.25rem" }} /> - </IconButton> - </Stack> + <IconButton + aria-label="back" + color="error" + sx={{ + display: "inline-flex", + position: "relative", + padding: "4px", + marginLeft: "4px" + }} + onClick={(e) => { + e.stopPropagation(); + remove(index); + }} + > + <ClearOutlinedIcon sx={{ fontSize: "1.25rem" }} /> + </IconButton> + </Stack> ); })} {/* <TagAtrributes control={control} /> */} diff --git a/dashboard/src/views/DashboardOverview/ClassificationCoverage.tsx b/dashboard/src/views/DashboardOverview/ClassificationCoverage.tsx index 21fc18588..03c27b94d 100644 --- a/dashboard/src/views/DashboardOverview/ClassificationCoverage.tsx +++ b/dashboard/src/views/DashboardOverview/ClassificationCoverage.tsx @@ -373,7 +373,12 @@ const ClassificationCoverage = memo( fontSize: "0.875rem", fontWeight: 500, cursor: "pointer", + display: "block", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap" }} + title={name} > {name} </Link> diff --git a/dashboard/src/views/DashboardOverview/RecentActivity.tsx b/dashboard/src/views/DashboardOverview/RecentActivity.tsx index fdd5be150..d51454899 100644 --- a/dashboard/src/views/DashboardOverview/RecentActivity.tsx +++ b/dashboard/src/views/DashboardOverview/RecentActivity.tsx @@ -326,7 +326,7 @@ const RecentActivity = memo(() => { "&:last-child": { borderBottom: "none" } }} > - <Typography component="span" sx={{ fontSize: "0.875rem", color: "#333" }}> + <Typography component="span" sx={{ fontSize: "0.875rem", color: "#333" }} title={entityName ? `${entityName} - ${actionPhrase}` : actionPhrase}> {entityName ? ( <> {typeDefPayload ? ( @@ -346,6 +346,12 @@ const RecentActivity = memo(() => { padding: 0, font: "inherit", "&:hover": { textDecoration: "underline" }, + display: "inline-block", + maxWidth: "300px", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + verticalAlign: "bottom" }} > {entityName} @@ -357,13 +363,19 @@ const RecentActivity = memo(() => { sx={{ color: "primary.main", textDecoration: "none", - "&:hover": { textDecoration: "underline" } + "&:hover": { textDecoration: "underline" }, + display: "inline-block", + maxWidth: "300px", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", + verticalAlign: "bottom" }} > {entityName} </Link> ) : ( - entityName + <Box component="span" className="text-truncate recent-activity-truncate">{entityName}</Box> )} {" "} {typeLabel} {actionPhrase} by{" "} diff --git a/dashboard/src/views/DetailPage/DetailPageAttributes.tsx b/dashboard/src/views/DetailPage/DetailPageAttributes.tsx index 6aac958b8..3bbe6fe40 100644 --- a/dashboard/src/views/DetailPage/DetailPageAttributes.tsx +++ b/dashboard/src/views/DetailPage/DetailPageAttributes.tsx @@ -134,17 +134,19 @@ const DetailPageAttribute = ({ className="detail-page-paper" variant="outlined" > - <Stack direction="row" justifyContent="space-between"> - <Typography - noWrap - fontWeight={600} - fontSize={"24px"} - component={"h1"} - data-id="title" - className="detail-page-enity-name mb-0 mt-0" - > - {name}{" "} - </Typography> + <Stack direction="row" justifyContent="space-between" gap="1rem" alignItems="flex-start"> + <LightTooltip title={name}> + <Typography + noWrap + fontWeight={600} + fontSize={"24px"} + component={"h1"} + data-id="title" + className="detail-page-enity-name mb-0 mt-0" + > + {name}{" "} + </Typography> + </LightTooltip> {isEmpty(bmguid) && ( <LightTooltip title={"Edit Classification"}> <CustomButton @@ -175,11 +177,11 @@ const DetailPageAttribute = ({ style={{ ...(isEmpty(bmguid) ? { - display: "grid", - gridTemplateColumns: "1fr 1fr", - gridGap: "1rem 2rem", - marginBottom: "0.75rem" - } + display: "grid", + gridTemplateColumns: "1fr 1fr", + gridGap: "1rem 2rem", + marginBottom: "0.75rem" + } : {}) }} > diff --git a/dashboard/src/views/DetailPage/EntityDetailTabs/AuditTableDetails.tsx b/dashboard/src/views/DetailPage/EntityDetailTabs/AuditTableDetails.tsx index 07722c327..2c7f93971 100644 --- a/dashboard/src/views/DetailPage/EntityDetailTabs/AuditTableDetails.tsx +++ b/dashboard/src/views/DetailPage/EntityDetailTabs/AuditTableDetails.tsx @@ -24,7 +24,10 @@ const updateName = (entityName: string, entity: object) => { extractKeyValueFromEntity(entity); return ( <> - <span className="audit-detail-name"> + <span + className="audit-detail-name text-truncate-inline" + title={!isEmpty(entity) ? `${name} (${entityName})` : entityName} + > Name: {!isEmpty(entity) ? `${name} (${entityName})` : entityName} </span> </> @@ -70,9 +73,13 @@ const AuditTableDetails = ({ componentProps, row }: any) => { let customAttr = parseDetailsObject.customAttributes; return ( <> - {name == "-" - ? parseDetailsObject.typeName - : updateName(name, {})} + <div className="text-truncate d-inline-block w-100 align-bottom" + title={name == "-" ? parseDetailsObject.typeName : name} + > + {name == "-" + ? parseDetailsObject.typeName + : updateName(name, {})} + </div> <Stack direction={"row"} gap={"1rem"} diff --git a/dashboard/src/views/DetailPage/EntityDetailTabs/ClassificationsTab.tsx b/dashboard/src/views/DetailPage/EntityDetailTabs/ClassificationsTab.tsx index 0fdd395f6..6b215d9ed 100644 --- a/dashboard/src/views/DetailPage/EntityDetailTabs/ClassificationsTab.tsx +++ b/dashboard/src/views/DetailPage/EntityDetailTabs/ClassificationsTab.tsx @@ -86,8 +86,8 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ let newClassifications = structuredClone(classifications); let options = !isEmpty(newClassifications) ? (checked ? newClassifications : tags?.self)?.map( - (obj: { typeName: string }) => obj.typeName - ) + (obj: { typeName: string }) => obj.typeName + ) : []; const classificationData = [...["All"], ...options].sort(); @@ -96,14 +96,14 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ ? customSortBy(newClassifications, ["typeName"]) : [] : !isEmpty(tags?.self) - ? customSortBy(tags?.self, ["typeName"]) - : []; + ? customSortBy(tags?.self, ["typeName"]) + : []; let tableData = !isEmpty(classificationName) ? classificationName == "All" ? data : data.filter( - (obj: { typeName: string }) => obj.typeName == classificationName - ) + (obj: { typeName: string }) => obj.typeName == classificationName + ) : data; const handleCloseTagModal = () => { @@ -164,11 +164,12 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ return ( <Stack direction="row" gap={1}> <Link - className="entity-name w-100 text-blue text-decoration-none" + className="entity-name w-100 text-blue text-decoration-none text-truncate-block min-w-0" to={{ pathname: href }} color={"primary"} + title={values.typeName} > {values.typeName} </Link> @@ -210,13 +211,19 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ <LightTooltip title={values?.typeName}> {" "} <Link - className="entity-name text-blue text-decoration-none" + className="entity-name text-blue text-decoration-none text-truncate-block" to={{ pathname: href }} color={"primary"} > - <Typography fontWeight="600"> {values?.typeName} </Typography> + <Typography + fontWeight="600" + noWrap + > + {" "} + {values?.typeName}{" "} + </Typography> </Link> </LightTooltip> ); @@ -251,27 +258,27 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ {(guid == values?.entityGuid || (guid != values?.entityGuid && values.entityStatus == "DELETED")) && ( - <LightTooltip title={"Delete Classification"}> - <CustomButton - variant="outlined" - color="success" - className="table-filter-btn assignTag" - size="small" - onClick={(e: React.MouseEvent<HTMLElement>) => { - e.stopPropagation(); - setOpenModal(true); - let { name } = extractKeyValueFromEntity(entity); - setCurrentValue({ - selectedValue: values.typeName, - assetName: name - }); - }} - data-cy="addTag" - > - <DeleteOutlinedIcon className="table-filter-refresh" /> - </CustomButton> - </LightTooltip> - )} + <LightTooltip title={"Delete Classification"}> + <CustomButton + variant="outlined" + color="success" + className="table-filter-btn assignTag" + size="small" + onClick={(e: React.MouseEvent<HTMLElement>) => { + e.stopPropagation(); + setOpenModal(true); + let { name } = extractKeyValueFromEntity(entity); + setCurrentValue({ + selectedValue: values.typeName, + assetName: name + }); + }} + data-cy="addTag" + > + <DeleteOutlinedIcon className="table-filter-refresh" /> + </CustomButton> + </LightTooltip> + )} {guid == values?.entityGuid && ( <LightTooltip title={"Edit Classification"}> <CustomButton @@ -319,14 +326,49 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ value={classificationName} onChange={(_e: any, newValue: string) => { handleChange(newValue as string); + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur(); + } }} isOptionEqualToValue={(option, value) => option === value} getOptionLabel={(option) => option} options={classificationData} id="entity-classification" className="classification-table-autocomplete" + componentsProps={{ + paper: { + sx: { + maxWidth: "100%", + overflowX: "hidden" + } + } + }} + renderOption={(props, option: any) => { + return ( + <li + {...props} + title={option} + className={props.className} + > + <span className="autocomplete-option-label"> + {option} + </span> + </li> + ); + }} renderInput={(params) => ( - <TextField {...params} label="Classifications" /> + <TextField + {...params} + label="Classifications" + title={classificationName || ""} + sx={{ + "& .MuiAutocomplete-input": { + textOverflow: "ellipsis !important", + overflow: "hidden !important", + whiteSpace: "nowrap !important" + } + }} + /> )} disableClearable /> @@ -389,8 +431,14 @@ const ClassificationsTab: React.FC<EntityDetailTabProps> = ({ button2Loading={removeAssignmentLoading} > <Typography fontSize={15}> - Remove: <b>{currentValue.selectedValue}</b> assignment from{" "} - <b>{currentValue.assetName}</b> ? + Remove:{" "} + <b + title={currentValue.selectedValue} + className="text-truncate-inline" + > + {currentValue.selectedValue} + </b>{" "} + assignment from <b>{currentValue.assetName}</b> ? </Typography> </CustomModal> )} diff --git a/dashboard/src/views/SideBar/Import/ImportLayout.tsx b/dashboard/src/views/SideBar/Import/ImportLayout.tsx index a0ef5e851..f7eb351b4 100644 --- a/dashboard/src/views/SideBar/Import/ImportLayout.tsx +++ b/dashboard/src/views/SideBar/Import/ImportLayout.tsx @@ -144,14 +144,11 @@ const ImportLayout = ({ <Typography component="span" sx={{ - overflow: "hidden", - whiteSpace: "nowrap", - textOverflow: "ellipsis", lineHeight: "2", width: "100%", display: "block" }} - > + noWrap> {file.name} </Typography> </LightTooltip> diff --git a/dashboard/src/views/Statistics/ClassificationStats.tsx b/dashboard/src/views/Statistics/ClassificationStats.tsx index 7f6e88aa3..d3ba8e38b 100644 --- a/dashboard/src/views/Statistics/ClassificationStats.tsx +++ b/dashboard/src/views/Statistics/ClassificationStats.tsx @@ -126,7 +126,9 @@ const ClassificationStats = ({ handleClose }: any) => { ) : ( Object.entries(tagEntitiesData).map(([key, value]: any) => ( <TableRow key={key}> - <TableCell sx={{ wordBreak: "break-all" }}>{key}</TableCell> + <TableCell className="classification-name-cell" title={key}> + <div className="text-truncate">{key}</div> + </TableCell> <TableCell align="right"> <LightTooltip title={`Search for entities associated with '${key}'`}
