diff --git a/src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSelector.tsx b/src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSelector.tsx index e25b8d947..0d075bd97 100644 --- a/src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSelector.tsx +++ b/src/components/navigation/GlobalNavigation/WorkspaceSelector/WorkspaceSelector.tsx @@ -9,13 +9,14 @@ import { type INavigationWorkspace, type IWorkspaceSelectorDisplayItem, Popover, + Typography, } from 'src/components' import { Flex } from 'src/components' import React, { type ChangeEvent, useRef, useState } from 'react' import { useCallback } from 'react' import { useEffect } from 'react' import { useMemo } from 'react' -import { debounce, hasImageAtSrc } from 'src/utils/utils' +import { debounce, hasContent, hasImageAtSrc, trimString } from 'src/utils/utils' import { getInitials } from 'src/utils/utils' import { type InputRef } from 'src/components' @@ -62,6 +63,9 @@ function sortOrgsByActiveWorkspace(orgs: INavigationOrg[]): INavigationOrg[] { return orgs } +/** Number of characters to show under the avatar */ +const WORKSPACE_LABEL_LIMIT = 7 + export function WorkspaceSelector(props: IWorkspaceSelectorProps) { const [searchTerm, setSearchTerm] = useState('') const inputRef = useRef(null) @@ -109,6 +113,9 @@ export function WorkspaceSelector(props: IWorkspaceSelectorProps) { }, [sortedOrgs]) const workspaceInitials = getInitials(activeWorkspace?.label) + const workspaceLabel = hasContent(activeWorkspace?.label) + ? trimString(activeWorkspace?.label, WORKSPACE_LABEL_LIMIT) + : undefined const hasSearchInput = !!searchTerm || menuItems.filter(item => !!item.label).length > 5 @@ -152,9 +159,12 @@ export function WorkspaceSelector(props: IWorkspaceSelectorProps) { onClick={e => { focusOnInput(true) }}> - - {getInitialsIfNoImage(hasImage, workspaceInitials)} - + + + {getInitialsIfNoImage(hasImage, workspaceInitials)} + + {workspaceLabel && {workspaceLabel}} + ) @@ -260,4 +270,4 @@ export function WorkspaceSelector(props: IWorkspaceSelectorProps) { } } } -} \ No newline at end of file +} diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts index c6cd4d924..b40064c01 100644 --- a/src/utils/utils.spec.ts +++ b/src/utils/utils.spec.ts @@ -1,4 +1,4 @@ -import { getInitials, getOS } from './utils' +import { getInitials, getOS, hasContent, trimString } from './utils' import { expect, describe, it, beforeEach, vi } from 'vitest' describe('Testing utils', () => { @@ -55,4 +55,73 @@ describe('Testing utils', () => { expect(actualOS).toBe('Macintosh') }) }) + describe('trimString()', () => { + it('should return the string when no limit given', () => { + const str = 'test string' + const result = trimString(str) + expect(result).toBe(str) + }) + it('should return the trimmed string when no limit given and spaces included', () => { + const str = 'test string ' + const result = trimString(str) + expect(result).toBe('test string') + }) + it('should return the shortened string when limit given', () => { + const str = 'test string' + const limit = 5 + const result = trimString(str, limit) + expect(result).toBe('test...') + }) + it('should return an empty string when input is undefined', () => { + const str = undefined + const result = trimString(str) + expect(result).toBe('') + }) + it('should return the string input when limit is not an integer', () => { + const str = 'test string' + const result = trimString(str) + expect(result).toBe(str) + }) + it('should return the string input when limit is undefined', () => { + const str = 'test string' + const limit = undefined + const result = trimString(str, limit) + expect(result).toBe(str) + }) + it('should return the string input when limit is not an integer', () => { + const str = 'test string' + const limit = 3.23 + const result = trimString(str, limit) + expect(result).toBe(str) + }) + it('should return the string input when limit is less than 0', () => { + const str = 'test string' + const limit = -5 + const result = trimString(str, limit) + expect(result).toBe(str) + }) + }) + + describe('hasContent()', () => { + it('should return true when string is valid', () => { + const str = 'test string' + const result = hasContent(str) + expect(result).toBe(true) + }) + it('should return false when string is empty', () => { + const str = '' + const result = hasContent(str) + expect(result).toBe(false) + }) + it('should return false when string is only spaces', () => { + const str = ' \n\t' + const result = hasContent(str) + expect(result).toBe(false) + }) + it('should return false when string is undefined', () => { + const str = undefined + const result = hasContent(str) + expect(result).toBe(false) + }) + }) }) diff --git a/src/utils/utils.tsx b/src/utils/utils.tsx index 82218aacb..665ebc3b2 100644 --- a/src/utils/utils.tsx +++ b/src/utils/utils.tsx @@ -63,3 +63,18 @@ export function buildLinkFromHrefOptions(label: ReactNode, hrefOptions?: HrefOpt ) } + +/** Returns `true` when a string has contents that are not just spaces */ +export const hasContent = (str?: string) => { + return (str?.trim?.()?.length ?? 0) > 0 +} + +/** Returns a string that is trimmed of extraneous spacing, and shortened to the `limit` if parameter provided/applicable */ +export const trimString = (str?: string, limit?: number) => { + const _str = str?.trim() ?? '' + const _limit = limit && Number.isInteger(limit) && limit >= 0 ? limit : undefined + if (_limit !== undefined && _str.length > _limit) { + return `${_str.substring(0, limit).trim()}...` + } + return _str +}