diff --git a/CHANGELOG.md b/CHANGELOG.md index 6936367ea..2d6c570b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# [1.21.0](https://github.com/mParticle/aquarium/compare/v1.20.0...v1.21.0) (2024-07-16) + +### Features + +- add workspace label to Workspace Selector ([#323](https://github.com/mParticle/aquarium/issues/323)) ([bdb3c70](https://github.com/mParticle/aquarium/commit/bdb3c70ebf8856f1e5cd436fc243ed47e335fbb4)) + # [1.20.0](https://github.com/mParticle/aquarium/compare/v1.19.3...v1.20.0) (2024-07-08) ### Features diff --git a/package-lock.json b/package-lock.json index a7ebff0b4..346c87413 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@mparticle/aquarium", - "version": "1.20.0", + "version": "1.21.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@mparticle/aquarium", - "version": "1.20.0", + "version": "1.21.0", "license": "Apache-2.0", "dependencies": { "lodash.clonedeep": "4.5.0" diff --git a/package.json b/package.json index 8520c8108..d41b88957 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mparticle/aquarium", - "version": "1.20.0", + "version": "1.21.0", "description": "mParticle Component Library", "license": "Apache-2.0", "keywords": [ diff --git a/release.config.cjs b/release.config.cjs index 5fa599deb..32117a64d 100644 --- a/release.config.cjs +++ b/release.config.cjs @@ -3,17 +3,17 @@ module.exports = { { name: 'feat/*', channel: 'feature', - prerelease: "${name.split('/').slice(1).join('-').toLowerCase()}" + prerelease: "${name.split('/').slice(1).join('-').toLowerCase().replaceAll('_', '-')}" }, { name: 'chore/*', channel: 'chore', - prerelease: "chore-${name.split('/').slice(1).join('-').toLowerCase()}" + prerelease: "chore-${name.split('/').slice(1).join('-').toLowerCase().replaceAll('_', '-')}" }, { name: 'fix/*', channel: 'fix', - prerelease: "fix-${name.split('/').slice(1).join('-').toLowerCase()}" + prerelease: "fix-${name.split('/').slice(1).join('-').toLowerCase().replaceAll('_', '-')}" }, ], diff --git a/src/components/data-entry/QueryItem/NumberInput.stories.tsx b/src/components/data-entry/QueryItem/NumberInput.stories.tsx index 0cdeaa3c3..d8a24aa9a 100644 --- a/src/components/data-entry/QueryItem/NumberInput.stories.tsx +++ b/src/components/data-entry/QueryItem/NumberInput.stories.tsx @@ -12,7 +12,7 @@ const meta: Meta = { }, }, args: { - onChange: (value: number) => { + onChange: (value: number | undefined) => { console.log(value) }, }, diff --git a/src/components/data-entry/QueryItem/NumberInput.tsx b/src/components/data-entry/QueryItem/NumberInput.tsx index 31b5bae25..5214b4ef1 100644 --- a/src/components/data-entry/QueryItem/NumberInput.tsx +++ b/src/components/data-entry/QueryItem/NumberInput.tsx @@ -2,8 +2,8 @@ import './query-item.css' import { InputNumber } from 'src/components' import { Typography } from 'src/components/general/Typography/Typography' -interface INumberInputProps { - value?: number +export interface INumberInputProps { + value?: number | undefined disabled?: boolean errorMessage?: string autoFocus: boolean @@ -11,7 +11,7 @@ interface INumberInputProps { min?: number max?: number step?: number - onChange?: (value: number) => void + onChange?: (value: number | undefined) => void onPressEnter?: (e: React.KeyboardEvent) => void } @@ -21,6 +21,11 @@ const NumberInput = (props: INumberInputProps) => { let inputClasses = `query-item query-item--input-number` if (props.errorMessage) inputClasses += ' query-item--error' + const handleOnChange = (value: string | number | null | undefined) => { + const floatValue = parseFloat(value as string) + isNaN(floatValue) ? props.onChange?.(undefined) : props.onChange?.(floatValue) + } + return ( <> { min={props.min} step={props.step} onPressEnter={props.onPressEnter} - onChange={(value: string | number | null) => { - props.onChange?.(parseFloat(value as string)) - }} + onChange={handleOnChange} /> {props.errorMessage && {props.errorMessage}} diff --git a/src/components/index.ts b/src/components/index.ts index a8bcb88dc..2eb83dd37 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -21,6 +21,7 @@ export { TimePicker, type ITimePickerProps } from './data-entry/TimePicker/TimeP export { Transfer, type ITransferProps } from './data-entry/Transfer/Transfer' export { QueryItem } from './data-entry/QueryItem/QueryItem' export type { IQueryItemQualifierOption } from './data-entry/QueryItem/Qualifier' +export type { INumberInputProps } from './data-entry/QueryItem/NumberInput' export { Collapse, type ICollapseProps } from './data-display/Collapse/Collapse' export { Timeline, type ITimelineProps } from './data-display/Timeline/Timeline' export { Calendar, type ICalendarProps } from './data-display/Calendar/Calendar' @@ -122,4 +123,4 @@ export { useNewExperienceReminder, type INewExperienceReminderOptions, type NewExperienceReminderHook, -} from '../hooks/NewExperienceReminder/useNewExperienceReminder' \ No newline at end of file +} from '../hooks/NewExperienceReminder/useNewExperienceReminder' 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 +}