Skip to content

Commit

Permalink
added tolerance and ZWNJ split to name
Browse files Browse the repository at this point in the history
  • Loading branch information
storywithoutend committed Apr 15, 2024
1 parent cfffeaa commit 6a2b1bc
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 46 deletions.
4 changes: 2 additions & 2 deletions src/components/@atoms/Name/Name.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ export const Name = ({
const initialWidth = containerWidth + containerLeft! - nodeRect.left
const ellipsisWidth = ellipsisRef.current?.offsetWidth || 0
return calculateWrapName({
name: children,
name_: children,
node,
ellipsisWidth,
maxWidth: containerWidth,
initialWidth,
lines: wrapLines,
maxLines: wrapLines,
})
})
.otherwise(() => {
Expand Down
51 changes: 46 additions & 5 deletions src/components/@atoms/Name/utils/calculateInlineName.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { calculateInlineName } from './calculateInlineName';
const jsdom = require('jsdom');
const { JSDOM } = jsdom;

const longLabel = 'areallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylonglabel'
const longName = 'areallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongname.eth'
const longSubname = `${longLabel}.${longName}`

const createNode = (str: string) => {
const chars = str.split('');
const innerHtml = chars.map((char) => `<span>${char}</span>`).join('')
Expand Down Expand Up @@ -38,14 +42,51 @@ const createNode = (str: string) => {
return dom.window.document.getElementById('root')
}

const removeNonNameSymbols = (str: string) => str.replace(/[\u2026\u200B\u200C]/g, '')

describe('calculateInlineName', () => {
it('should return null if the node is null', () => {
it('should return the correct result if the first label ends on the right side', () => {
const result = calculateInlineName({
name: longName,
node: createNode(longName),
ellipsisWidth: 5,
maxWidth: 100,
})
expect(result).toBe('areallyre…​gname‌.eth')

Check failure on line 55 in src/components/@atoms/Name/utils/calculateInlineName.test.ts

View workflow job for this annotation

GitHub Actions / coverage

src/components/@atoms/Name/utils/calculateInlineName.test.ts > calculateInlineName > should return the correct result if the first label ends on the right side

AssertionError: expected 'areallyr…​name\u200c.eth' to be 'areallyre…​gname\u200c.eth' // Object.is equality - Expected + Received - areallyre…​gname‌.eth + areallyr…​name‌.eth ❯ src/components/@atoms/Name/utils/calculateInlineName.test.ts:55:20
const ZWNJSplit = result.split('\u200C')
expect(ZWNJSplit).toHaveLength(2)
const ZWSSplit = result.split('\u200B')
console.log(removeNonNameSymbols(ZWSSplit[0]), removeNonNameSymbols(ZWSSplit[1]))
expect(removeNonNameSymbols(ZWSSplit[0]).length).toBe(removeNonNameSymbols(ZWSSplit[1]).length)
})

it('should return the correct result if the first label ends on the left side', () => {
const shortSubname = `test.${longName}`
const result = calculateInlineName({
name: shortSubname,
node: createNode(shortSubname),
ellipsisWidth: 5,
maxWidth: 100,
})
expect(result).toBe('test\u200C.area…​gname.eth')

Check failure on line 71 in src/components/@atoms/Name/utils/calculateInlineName.test.ts

View workflow job for this annotation

GitHub Actions / coverage

src/components/@atoms/Name/utils/calculateInlineName.test.ts > calculateInlineName > should return the correct result if the first label ends on the left side

AssertionError: expected 'test\u200c.are…​name.eth' to be 'test\u200c.area…​gname.eth' // Object.is equality - Expected + Received - test‌.area…​gname.eth + test‌.are…​name.eth ❯ src/components/@atoms/Name/utils/calculateInlineName.test.ts:71:20
const ZWNJSplit = result.split('\u200C')
expect(ZWNJSplit).toHaveLength(2)
const ZWSSplit = result.split('\u200B')
console.log(removeNonNameSymbols(ZWSSplit[0]), removeNonNameSymbols(ZWSSplit[1]))
expect(removeNonNameSymbols(ZWSSplit[0]).length).toBe(removeNonNameSymbols(ZWSSplit[1]).length)
})

it('should return the correct result if the first label ends in the middle', () => {
const result = calculateInlineName({
name: 'helloworld!',
node: createNode('helloworld!'),
name: longSubname,
node: createNode(longSubname),
ellipsisWidth: 5,
maxWidth: 30,
maxWidth: 100,
})
expect(result).toBe('he…​d!')
expect(result).toBe('areallyre…\u200C\u200Bgname.eth')

Check failure on line 86 in src/components/@atoms/Name/utils/calculateInlineName.test.ts

View workflow job for this annotation

GitHub Actions / coverage

src/components/@atoms/Name/utils/calculateInlineName.test.ts > calculateInlineName > should return the correct result if the first label ends in the middle

AssertionError: expected 'areallyr…\u200c​name.eth' to be 'areallyre…\u200c​gname.eth' // Object.is equality - Expected + Received - areallyre…‌​gname.eth + areallyr…‌​name.eth ❯ src/components/@atoms/Name/utils/calculateInlineName.test.ts:86:20
const ZWNJSplit = result.split('\u200C')
expect(ZWNJSplit).toHaveLength(2)
const ZWSSplit = result.split('\u200B')
expect(removeNonNameSymbols(ZWSSplit[0]).length).toBe(removeNonNameSymbols(ZWSSplit[1]).length)
})
})
34 changes: 25 additions & 9 deletions src/components/@atoms/Name/utils/calculateInlineName.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,33 @@
import { insertZeroWidthNonJoinerAtLabel } from './sharedFunctions'

export const calculateInlineName = ({
name,
node,
ellipsisWidth,
maxWidth,
tolerance = 5,
debug = false,
}: {
name: string
node: HTMLSpanElement | null
ellipsisWidth: number
maxWidth: number
tolerance?: number
debug?: boolean
}) => {
if (debug) console.log('calculateInlineName', name, node, ellipsisWidth, maxWidth)
if (!node) return name
const _name = insertZeroWidthNonJoinerAtLabel(name)
if (debug) console.log('calculateInlineName', _name, node, ellipsisWidth, maxWidth)
if (!node) return _name

const parentElementWidth = maxWidth ?? node.parentElement?.offsetWidth ?? Infinity
const _maxWidth = maxWidth ?? node.parentElement?.offsetWidth ?? Infinity
const nodeWidth = node.offsetWidth || Infinity

if (debug) console.log('nodeWidth', nodeWidth, 'parentElementWidth', parentElementWidth)
if (nodeWidth <= parentElementWidth) return name
if (debug) console.log('nodeWidth', nodeWidth, 'parentElementWidth', _maxWidth)
if (nodeWidth <= _maxWidth) return _name

// We use a tolerance because the offsetWidth of the individual characters are rounded to the nearest integer, which creates a potential for inaccuracies.
const _tolerance = 1 - Math.max(0, Math.min(100, tolerance)) / 100
const maxWidthWithTolerance = _maxWidth * _tolerance

const children = node?.children || []
let slice = 0
Expand All @@ -27,10 +36,17 @@ export const calculateInlineName = ({
const element = children[index] as HTMLSpanElement
const matchElement = children[children.length - 1 - index] as HTMLSpanElement
total += element.offsetWidth + matchElement.offsetWidth
if (total > parentElementWidth)
return `${name.slice(0, slice)}\u2026\u200B${name.slice(name.length - slice)}`
if (total >= maxWidthWithTolerance) {
const right = _name.slice(_name.length - slice)
if (right.includes('\u200C'))
return `${_name.slice(0, slice)}\u2026\u200B${_name.slice(_name.length - slice - 1)}`
const left = _name.slice(0, slice)
if (left.includes('\u200C'))
return `${_name.slice(0, slice + 1)}\u2026\u200B${_name.slice(_name.length - slice)}`
return `${left}\u2026\u200C\u200B${right}`
}
slice += 1
}
if (debug) console.log('name', name)
return name
if (debug) console.log('name', _name)
return _name
}
39 changes: 26 additions & 13 deletions src/components/@atoms/Name/utils/calculateWrapName.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const createNode = (str: string) => {
return dom.window.document.getElementById('root')
}

const removeSpecialCharacters = (str: string) => str.replace(/[\u2026\u200B\u200C]/g, '')

describe('findNumbersAddingUpToSum', () => {
it('should return numbers that add up just below the sum', () => {
const result = findNumbersAddingUpToSum([1, 2, 3, 4, 5], 7)
Expand Down Expand Up @@ -63,27 +65,38 @@ describe('sliceStringByNumbers', () => {
})

describe('calculateWrapName', () => {
it('should return the correct result', () => {
const longName = 'areallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongname.eth'
it.only('should return the correct result', () => {
const result = calculateWrapName({
name: 'helloworld!',
node: createNode('helloworld!'),
name: longName,
node: createNode(longName),
ellipsisWidth: 5,
initialWidth: 10,
maxWidth: 20,
lines: Infinity
initialWidth: 100,
maxWidth: 500,
maxLines: Infinity
})
expect(result).toEqual('h…​ell…​owo…​rld…​!')
expect(result).toEqual('areallyreallyreally…​reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyrea…​llyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally…​reallylongname.eth')
console.log('areallyreallyreally…​reallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyrea…​llyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreally…​eallylongname‌.eth')
const resultParts = result.split('…​')
expect(resultParts[0]).toHaveLength(19)
expect(resultParts[1]).toHaveLength(99)
expect(resultParts[2]).toHaveLength(99)
expect(resultParts[3]).toHaveLength(longName.length - 19 - 99 - 99)
})

it('should return the correct result', () => {
const result = calculateWrapName({
name: 'helloworld!',
node: createNode('helloworld!'),
name: longName,
node: createNode(longName),
ellipsisWidth: 5,
initialWidth: 10,
maxWidth: 20,
lines: 2
initialWidth: 100,
maxWidth: 500,
maxLines: 2
})
expect(result).toEqual('h…​rld!')
expect(result).toEqual('areallyreallyreally…​allyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongname\u200C.eth')
console.log('areallyreallyreally…​llyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallyreallylongname‌.eth')
const resultParts = result.split('…​')
expect(resultParts[0]).toHaveLength(19)
expect(resultParts[1]).toHaveLength(100)
})
})
40 changes: 27 additions & 13 deletions src/components/@atoms/Name/utils/calculateWrapName.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { insertZeroWidthNonJoinerAtLabel } from './sharedFunctions'

export const findNumbersAddingUpToSum = (numbers: number[], sum: number) => {
let index = 0
let total = 0
Expand Down Expand Up @@ -30,16 +32,18 @@ export const calculateWrapName = ({
maxWidth,
initialWidth = maxWidth,
minInitialWidth = 0,
lines = Infinity,
maxLines = Infinity,
tolerance = 5,
debug = false,
}: {
name: string
node: HTMLSpanElement | null
ellipsisWidth: number
maxWidth?: number
maxWidth: number
initialWidth?: number
minInitialWidth?: number
lines?: number
maxLines?: number
tolerance?: number
debug?: boolean
}): string => {
if (debug)
Expand All @@ -51,28 +55,38 @@ export const calculateWrapName = ({
maxWidth,
initialWidth,
minInitialWidth,
lines,
maxLines,
)

const name_ = insertZeroWidthNonJoinerAtLabel(name)
if (!node) {
console.error('node is null')
return name
return name_
}

const _maxWdth = maxWidth ?? node.parentElement?.offsetWidth ?? Infinity
const containerWidth = node.offsetWidth || Infinity
if (containerWidth <= _maxWdth) return name_

let currentGroup: number[] = []
let currentGroupTotal = 0
let result: number[][] = []

const initialWidth_ = initialWidth < minInitialWidth ? maxWidth : initialWidth

const decimalTolerance = 1 - Math.max(0, Math.min(100, tolerance)) / 100
const initialWidthWithTolerance = initialWidth_ * decimalTolerance
const maxWidthWithTolerance = maxWidth * decimalTolerance

const children = node?.children || []
for (let index = 0; index < children.length; index += 1) {
const element = children[index] as HTMLSpanElement
const charWidth = element.offsetWidth
currentGroupTotal += charWidth
const breakpoint = result.length === 0 ? initialWidth_ : maxWidth
const currentMaxWidth = result.length === 0 ? initialWidthWithTolerance : maxWidthWithTolerance
if (debug)
console.log('charWidth', charWidth, 'currentGroupTotal', currentGroupTotal, breakpoint)
if (currentGroupTotal + ellipsisWidth > breakpoint) {
console.log('charWidth', charWidth, 'currentGroupTotal', currentGroupTotal, currentMaxWidth)
if (currentGroupTotal + ellipsisWidth >= currentMaxWidth) {
result.push(currentGroup)
currentGroup = [charWidth]
currentGroupTotal = charWidth
Expand All @@ -83,10 +97,10 @@ export const calculateWrapName = ({
if (currentGroup.length) result.push(currentGroup)

// console.log(result.length, lines)
if (result.length > lines) {
const left = result.slice(0, lines - 1)
if (result.length > maxLines) {
const left = result.slice(0, maxLines - 1)
const right = result
.slice(lines - 1)
.slice(maxLines - 1)
.reverse()
.flat()
// console.log('left', left, right)
Expand All @@ -97,7 +111,7 @@ export const calculateWrapName = ({
const slices = result.map((group) => group.length)
const [last, ...reversedFirstSegments] = slices.reverse()
const firstSegments = reversedFirstSegments.reverse()
const firstNames = sliceStringByNumbers(firstSegments, name)
const lastSegment = name.slice(-last)
const firstNames = sliceStringByNumbers(firstSegments, name_)
const lastSegment = name_.slice(-last)
return [...firstNames, lastSegment].join('\u2026\u200B')
}
23 changes: 23 additions & 0 deletions src/components/@atoms/Name/utils/sharedFunctions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, it, expect } from 'vitest'

import { insertZeroWidthNonJoinerAtLabel } from './sharedFunctions'


describe('insertZeroWidthNonJoinerAtLabel', () => {
it('should insert a ZWNJ after the first label', () => {
const name = insertZeroWidthNonJoinerAtLabel('name.com')
expect(name).toBe('name\u200C.com')
})

it('should insert a ZWNJ after the first label with multiple labels', () => {
const name = insertZeroWidthNonJoinerAtLabel('name.co.uk')
expect(name).toBe('name\u200C.co.uk')
})

it('should be able to split resulting name by ZWNJ', () => {
const name = insertZeroWidthNonJoinerAtLabel('name.co.uk')
const [label, ...rest] = name.split('\u200C')
expect(label).toBe('name')
expect(rest.join('')).toBe('.co.uk')
})
})
4 changes: 4 additions & 0 deletions src/components/@atoms/Name/utils/sharedFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const insertZeroWidthNonJoinerAtLabel = (name: string) => {
const [label, ...rest] = name.split('.')
return [`${label}\u200C`, ...rest].join('.')
}
4 changes: 2 additions & 2 deletions src/components/@atoms/Name2/Name.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ export const Name = ({
const initialWidth_ = initialWidth ?? maxWidth_ - hiddenLeft + rootLeft

return calculateWrapName({
name: children,
name_: children,
node: hiddenRef.current,
ellipsisWidth,
maxWidth: Math.round(maxWidth_ * 0.95),
initialWidth: Math.round(initialWidth_ * 0.95),
minInitialWidth,
lines: wrapLines,
maxLines: wrapLines,
debug,
})
})
Expand Down
1 change: 1 addition & 0 deletions src/components/@atoms/StyledName/StyledName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Container = styled.div(
font-weight: ${theme.fontWeights.bold};
line-height: 1.36;
overflow: hidden;
background: yellow;
`,
)

Expand Down
2 changes: 1 addition & 1 deletion src/components/@molecules/NameListView/NameListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export const NameListView = ({ address, selfAddress, setError, setLoading }: Nam
)

const isLoading = isNamesLoading || !address

console.log('names', names)
let InnerContent: ReactNode
if (!isMounted) {
InnerContent = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ const TitleContainer = styled.div(
`,
)

const NameContainer = styled.span(
() => css`
word-break: break-all;
`,
)

const Title = styled(Typography)(
({ theme }) => css`
font-size: ${theme.fontSizes.headingOne};
Expand Down Expand Up @@ -282,7 +288,10 @@ const Complete = ({ name, beautifiedName, callback, isMoonpayFlow }: Props) => {
<Title>{t('steps.complete.heading')}</Title>
<Typography style={{ display: 'inline' }} fontVariant="headingThree" weight="bold">
{t('steps.complete.subheading')}
<SubtitleWithGradient>{nameWithColourEmojis}</SubtitleWithGradient>
<br />
<NameContainer>
<SubtitleWithGradient>{nameWithColourEmojis}</SubtitleWithGradient>
</NameContainer>
</Typography>
</TitleContainer>
<Typography>{t('steps.complete.description')}</Typography>
Expand Down

0 comments on commit 6a2b1bc

Please sign in to comment.