diff --git a/frontend/src/component/admin/serviceAccounts/ServiceAccountsTable/ServiceAccountTokensCell/ServiceAccountTokensCell.tsx b/frontend/src/component/admin/serviceAccounts/ServiceAccountsTable/ServiceAccountTokensCell/ServiceAccountTokensCell.tsx index cfdf5d90d96..40023a59562 100644 --- a/frontend/src/component/admin/serviceAccounts/ServiceAccountsTable/ServiceAccountTokensCell/ServiceAccountTokensCell.tsx +++ b/frontend/src/component/admin/serviceAccounts/ServiceAccountsTable/ServiceAccountTokensCell/ServiceAccountTokensCell.tsx @@ -1,22 +1,16 @@ import { VFC } from 'react'; -import { Link, styled, Typography } from '@mui/material'; +import { styled, Typography } from '@mui/material'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; -import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { Highlighter } from 'component/common/Highlighter/Highlighter'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; import { IServiceAccount } from 'interfaces/service-account'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; +import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; const StyledItem = styled(Typography)(({ theme }) => ({ fontSize: theme.fontSizes.smallerBody, })); -const StyledLink = styled(Link, { - shouldForwardProp: prop => prop !== 'highlighted', -})<{ highlighted?: boolean }>(({ theme, highlighted }) => ({ - backgroundColor: highlighted ? theme.palette.highlight : 'transparent', -})); - interface IServiceAccountTokensCellProps { serviceAccount: IServiceAccount; value: string; @@ -35,8 +29,8 @@ export const ServiceAccountTokensCell: VFC = ({ return ( - {serviceAccount.tokens?.map(({ id, description }) => ( @@ -47,19 +41,15 @@ export const ServiceAccountTokensCell: VFC = ({ ))} } + highlighted={ + searchQuery.length > 0 && + value.toLowerCase().includes(searchQuery.toLowerCase()) + } > - 0 && - value.toLowerCase().includes(searchQuery.toLowerCase()) - } - > - {serviceAccount.tokens?.length === 1 - ? '1 token' - : `${serviceAccount.tokens?.length} tokens`} - - + {serviceAccount.tokens?.length === 1 + ? '1 token' + : `${serviceAccount.tokens?.length} tokens`} + ); }; diff --git a/frontend/src/component/common/HtmlTooltip/HtmlTooltip.tsx b/frontend/src/component/common/HtmlTooltip/HtmlTooltip.tsx index 580db9b1175..d36f2eaf1d3 100644 --- a/frontend/src/component/common/HtmlTooltip/HtmlTooltip.tsx +++ b/frontend/src/component/common/HtmlTooltip/HtmlTooltip.tsx @@ -1,29 +1,44 @@ import { styled, Tooltip, tooltipClasses, TooltipProps } from '@mui/material'; +import { SpacingArgument } from '@mui/system/createTheme/createSpacing'; -const StyledHtmlTooltip = styled(({ className, ...props }: TooltipProps) => ( - -))(({ theme }) => ({ - maxWidth: theme.spacing(37.5), - [`& .${tooltipClasses.tooltip}`]: { - display: 'flex', - flexDirection: 'column', - backgroundColor: theme.palette.background.paper, - padding: theme.spacing(1, 1.5), - borderRadius: theme.shape.borderRadiusMedium, - boxShadow: theme.shadows[2], - color: theme.palette.text.primary, - fontWeight: theme.fontWeight.medium, - maxWidth: 'inherit', - border: `1px solid ${theme.palette.lightBorder}`, - }, - [`& .${tooltipClasses.arrow}`]: { - '&:before': { +const StyledHtmlTooltip = styled( + ({ className, maxWidth, maxHeight, ...props }: IHtmlTooltipProps) => ( + + ), + { + shouldForwardProp: prop => prop !== 'maxWidth' && prop !== 'maxHeight', + } +)<{ maxWidth?: SpacingArgument; maxHeight?: SpacingArgument }>( + ({ theme, maxWidth, maxHeight }) => ({ + maxWidth: maxWidth || theme.spacing(37.5), + [`& .${tooltipClasses.tooltip}`]: { + display: 'flex', + flexDirection: 'column', + backgroundColor: theme.palette.background.paper, + padding: theme.spacing(1, 1.5), + borderRadius: theme.shape.borderRadiusMedium, + boxShadow: theme.shadows[2], + color: theme.palette.text.primary, + fontWeight: theme.fontWeight.medium, + maxWidth: 'inherit', border: `1px solid ${theme.palette.lightBorder}`, + maxHeight: maxHeight || theme.spacing(37.5), + overflow: 'auto', }, - color: theme.palette.background.paper, - }, -})); + [`& .${tooltipClasses.arrow}`]: { + '&:before': { + border: `1px solid ${theme.palette.lightBorder}`, + }, + color: theme.palette.background.paper, + }, + }) +); + +export interface IHtmlTooltipProps extends TooltipProps { + maxWidth?: SpacingArgument; + maxHeight?: SpacingArgument; +} -export const HtmlTooltip = (props: TooltipProps) => ( +export const HtmlTooltip = (props: IHtmlTooltipProps) => ( {props.children} ); diff --git a/frontend/src/component/common/Table/cells/FeatureTagCell/FeatureTagCell.tsx b/frontend/src/component/common/Table/cells/FeatureTagCell/FeatureTagCell.tsx index 2ecdbdaf10b..c50676fe817 100644 --- a/frontend/src/component/common/Table/cells/FeatureTagCell/FeatureTagCell.tsx +++ b/frontend/src/component/common/Table/cells/FeatureTagCell/FeatureTagCell.tsx @@ -1,21 +1,15 @@ import { VFC } from 'react'; import { FeatureSchema } from 'openapi'; -import { Link, styled, Typography } from '@mui/material'; +import { styled, Typography } from '@mui/material'; import { TextCell } from '../TextCell/TextCell'; -import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { Highlighter } from 'component/common/Highlighter/Highlighter'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; const StyledTag = styled(Typography)(({ theme }) => ({ fontSize: theme.fontSizes.smallerBody, })); -const StyledLink = styled(Link, { - shouldForwardProp: prop => prop !== 'highlighted', -})<{ highlighted?: boolean }>(({ theme, highlighted }) => ({ - backgroundColor: highlighted ? theme.palette.highlight : 'transparent', -})); - interface IFeatureTagCellProps { row: { original: FeatureSchema; @@ -31,8 +25,12 @@ export const FeatureTagCell: VFC = ({ row, value }) => { return ( - 0 && + value.toLowerCase().includes(searchQuery.toLowerCase()) + } + tooltip={ <> {row.original.tags?.map(tag => ( @@ -44,18 +42,10 @@ export const FeatureTagCell: VFC = ({ row, value }) => { } > - 0 && - value.toLowerCase().includes(searchQuery.toLowerCase()) - } - > - {row.original.tags?.length === 1 - ? '1 tag' - : `${row.original.tags?.length} tags`} - - + {row.original.tags?.length === 1 + ? '1 tag' + : `${row.original.tags?.length} tags`} + ); }; diff --git a/frontend/src/component/common/TooltipLink/TooltipLink.tsx b/frontend/src/component/common/TooltipLink/TooltipLink.tsx new file mode 100644 index 00000000000..6ab54948656 --- /dev/null +++ b/frontend/src/component/common/TooltipLink/TooltipLink.tsx @@ -0,0 +1,34 @@ +import { ReactNode } from 'react'; +import { Link, LinkProps, styled, TooltipProps } from '@mui/material'; +import { HtmlTooltip, IHtmlTooltipProps } from '../HtmlTooltip/HtmlTooltip'; + +const StyledLink = styled(Link, { + shouldForwardProp: prop => prop !== 'highlighted', +})<{ highlighted?: boolean }>(({ theme, highlighted }) => ({ + backgroundColor: highlighted ? theme.palette.highlight : 'transparent', + color: theme.palette.text.primary, + textDecorationColor: theme.palette.text.disabled, + textDecorationStyle: 'dashed', + textUnderlineOffset: theme.spacing(0.5), +})); + +interface ITooltipLinkProps extends LinkProps { + tooltip: ReactNode; + highlighted?: boolean; + tooltipProps?: Omit; + children: ReactNode; +} + +export const TooltipLink = ({ + tooltip, + highlighted, + tooltipProps, + children, + ...props +}: ITooltipLinkProps) => ( + + + {children} + + +); diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/OverridesCell/OverridesCell.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/OverridesCell/OverridesCell.tsx index cc565a10615..4e879aedd54 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/OverridesCell/OverridesCell.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/OverridesCell/OverridesCell.tsx @@ -1,20 +1,14 @@ -import { Link, styled, Typography } from '@mui/material'; +import { styled, Typography } from '@mui/material'; import { Highlighter } from 'component/common/Highlighter/Highlighter'; -import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; import { IOverride } from 'interfaces/featureToggle'; const StyledItem = styled(Typography)(({ theme }) => ({ fontSize: theme.fontSizes.smallerBody, })); -const StyledLink = styled(Link, { - shouldForwardProp: prop => prop !== 'highlighted', -})<{ highlighted?: boolean }>(({ theme, highlighted }) => ({ - backgroundColor: highlighted ? theme.palette.highlight : 'transparent', -})); - interface IOverridesCellProps { value?: IOverride[]; } @@ -29,8 +23,8 @@ export const OverridesCell = ({ value: overrides }: IOverridesCellProps) => { return ( - {overrides.map((override, index) => ( @@ -41,23 +35,19 @@ export const OverridesCell = ({ value: overrides }: IOverridesCellProps) => { ))} } + highlighted={ + searchQuery.length > 0 && + overrides + ?.map(overrideToString) + .join('\n') + .toLowerCase() + .includes(searchQuery.toLowerCase()) + } > - 0 && - overrides - ?.map(overrideToString) - .join('\n') - .toLowerCase() - .includes(searchQuery.toLowerCase()) - } - > - {overrides.length === 1 - ? '1 override' - : `${overrides.length} overrides`} - - + {overrides.length === 1 + ? '1 override' + : `${overrides.length} overrides`} + ); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/PayloadCell/PayloadCell.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/PayloadCell/PayloadCell.tsx index e9beea0b9dc..869aec393a9 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/PayloadCell/PayloadCell.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/PayloadCell/PayloadCell.tsx @@ -1,8 +1,8 @@ -import { Link, styled, Typography } from '@mui/material'; +import { styled, Typography } from '@mui/material'; import { Highlighter } from 'component/common/Highlighter/Highlighter'; -import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; import { IPayload } from 'interfaces/featureToggle'; const StyledItem = styled(Typography)(({ theme }) => ({ @@ -10,12 +10,6 @@ const StyledItem = styled(Typography)(({ theme }) => ({ whiteSpace: 'pre-wrap', })); -const StyledLink = styled(Link, { - shouldForwardProp: prop => prop !== 'highlighted', -})<{ highlighted?: boolean }>(({ theme, highlighted }) => ({ - backgroundColor: highlighted ? theme.palette.highlight : 'transparent', -})); - interface IPayloadCellProps { value?: IPayload; } @@ -35,8 +29,8 @@ export const PayloadCell = ({ value: payload }: IPayloadCellProps) => { return ( - @@ -45,19 +39,15 @@ export const PayloadCell = ({ value: payload }: IPayloadCellProps) => { } + highlighted={ + searchQuery.length > 0 && + payload.value + .toLowerCase() + .includes(searchQuery.toLowerCase()) + } > - 0 && - payload.value - .toLowerCase() - .includes(searchQuery.toLowerCase()) - } - > - {payload.type} - - + {payload.type} + ); }; diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescriptionEnvironmentPermissions/ProjectRoleDescriptionEnvironmentPermissions.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescriptionEnvironmentPermissions/ProjectRoleDescriptionEnvironmentPermissions.tsx index 1f871a33718..6c0701278d9 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescriptionEnvironmentPermissions/ProjectRoleDescriptionEnvironmentPermissions.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescriptionEnvironmentPermissions/ProjectRoleDescriptionEnvironmentPermissions.tsx @@ -8,12 +8,19 @@ export const ProjectRoleDescriptionEnvironmentPermissions = ({ permissions, }: IProjectRoleDescriptionEnvironmentPermissionsProps) => ( <> - {permissions - .filter((permission: any) => permission.environment === environment) - .map((permission: any) => permission.displayName) + {[ + ...new Set( + permissions + .filter( + (permission: any) => + permission.environment === environment + ) + .map((permission: any) => permission.displayName) + ), + ] .sort() .map((permission: any) => ( -

{permission}

+

{permission}

))} ); diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessRoleCell/ProjectAccessRoleCell.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessRoleCell/ProjectAccessRoleCell.tsx index e3ad78e73e1..661ed727b77 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessRoleCell/ProjectAccessRoleCell.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessRoleCell/ProjectAccessRoleCell.tsx @@ -1,19 +1,7 @@ -import { Link, Popover, styled } from '@mui/material'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; -import React from 'react'; import { VFC } from 'react'; import { ProjectRoleDescription } from 'component/project/ProjectAccess/ProjectAccessAssign/ProjectRoleDescription/ProjectRoleDescription'; - -const StyledLink = styled(Link)(() => ({ - textDecoration: 'none', - '&:hover, &:focus': { - textDecoration: 'underline', - }, -})); - -const StyledPopover = styled(Popover)(() => ({ - pointerEvents: 'none', -})); +import { TooltipLink } from 'component/common/TooltipLink/TooltipLink'; interface IProjectAccessRoleCellProps { roleId: number; @@ -28,49 +16,25 @@ export const ProjectAccessRoleCell: VFC = ({ value, emptyText, }) => { - const [anchorEl, setAnchorEl] = React.useState(null); - - const onPopoverOpen = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const onPopoverClose = () => { - setAnchorEl(null); - }; - if (!value) return {emptyText}; return ( - <> - - { - onPopoverOpen(event); - }} - onMouseLeave={onPopoverClose} - > - {value} - - - + + } + tooltipProps={{ + maxWidth: 500, + maxHeight: 600, }} > - - - + {value} + + ); };