Skip to content

Commit

Permalink
fix: unify class name splitting/filtering with normalizeClassNames
Browse files Browse the repository at this point in the history
  • Loading branch information
etrepum committed Feb 22, 2024
1 parent 21d5447 commit 57068e5
Show file tree
Hide file tree
Showing 10 changed files with 70 additions and 22 deletions.
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ module.name_mapper='^@lexical/yjs$' -> '<PROJECT_ROOT>/packages/lexical-yjs/flow

module.name_mapper='^shared/simpleDiffWithCursor' -> '<PROJECT_ROOT>/packages/shared/src/simpleDiffWithCursor.js'
module.name_mapper='^shared/invariant' -> '<PROJECT_ROOT>/packages/shared/src/invariant.js'
module.name_mapper='^shared/normalizeClassNames' -> '<PROJECT_ROOT>/packages/shared/src/normalizeClassNames.js'
module.name_mapper='^shared/warnOnlyOnce' -> '<PROJECT_ROOT>/packages/shared/src/warnOnlyOnce.js'
module.name_mapper='^shared/environment' -> '<PROJECT_ROOT>/packages/shared/src/environment.js'
module.name_mapper='^shared/useLayoutEffect' -> '<PROJECT_ROOT>/packages/shared/src/useLayoutEffect.js'
Expand Down
2 changes: 2 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ module.exports = {
'<rootDir>/packages/shared/src/caretFromPoint.ts',
'^shared/environment$': '<rootDir>/packages/shared/src/environment.ts',
'^shared/invariant$': '<rootDir>/packages/shared/src/invariant.ts',
'^shared/normalizeClassNames$':
'<rootDir>/packages/shared/src/normalizeClassNames.ts',
'^shared/simpleDiffWithCursor$':
'<rootDir>/packages/shared/src/simpleDiffWithCursor.ts',
'^shared/useLayoutEffect$':
Expand Down
6 changes: 3 additions & 3 deletions packages/lexical-list/src/LexicalListItemNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
LexicalEditor,
} from 'lexical';
import invariant from 'shared/invariant';
import normalizeClassNames from 'shared/normalizeClassNames';

import {$createListNode, $isListNode} from './';
import {$handleIndent, $handleOutdent, mergeLists} from './formatList';
Expand Down Expand Up @@ -434,8 +435,7 @@ function $setListItemThemeClassNames(
}

if (listItemClassName !== undefined) {
const listItemClasses = listItemClassName.split(' ');
classesToAdd.push(...listItemClasses);
classesToAdd.push(...normalizeClassNames(listItemClassName));
}

if (listTheme) {
Expand All @@ -460,7 +460,7 @@ function $setListItemThemeClassNames(
}

if (nestedListItemClassName !== undefined) {
const nestedListItemClasses = nestedListItemClassName.split(' ');
const nestedListItemClasses = normalizeClassNames(nestedListItemClassName);

if (node.getChildren().some((child) => $isListNode(child))) {
classesToAdd.push(...nestedListItemClasses);
Expand Down
6 changes: 3 additions & 3 deletions packages/lexical-list/src/LexicalListNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
Spread,
} from 'lexical';
import invariant from 'shared/invariant';
import normalizeClassNames from 'shared/normalizeClassNames';

import {$createListItemNode, $isListItemNode, ListItemNode} from '.';
import {updateChildrenListItemValue} from './formatList';
Expand Down Expand Up @@ -243,8 +244,7 @@ function setListThemeClassNames(
}

if (listLevelClassName !== undefined) {
const listItemClasses = listLevelClassName.split(' ');
classesToAdd.push(...listItemClasses);
classesToAdd.push(...normalizeClassNames(listLevelClassName));
for (let i = 0; i < listLevelsClassNames.length; i++) {
if (i !== normalizedListDepth) {
classesToRemove.push(node.__tag + i);
Expand All @@ -253,7 +253,7 @@ function setListThemeClassNames(
}

if (nestedListClassName !== undefined) {
const nestedListItemClasses = nestedListClassName.split(' ');
const nestedListItemClasses = normalizeClassNames(nestedListClassName);

if (listDepth > 1) {
classesToAdd.push(...nestedListItemClasses);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,16 @@ describe('LexicalElementHelpers tests', () => {

test('empty', async () => {
const element = document.createElement('div');
addClassNamesToElement(element, null, undefined, false, true, '');
addClassNamesToElement(
element,
null,
undefined,
false,
true,
'',
' ',
' \t\n',
);

expect(element.className).toEqual('');
});
Expand All @@ -53,4 +62,16 @@ describe('LexicalElementHelpers tests', () => {
expect(element.className).toEqual('');
});
});

test('multiple spaces', async () => {
const classNames = ' a b c \t\n ';
const element = document.createElement('div');
addClassNamesToElement(element, classNames);

expect(element.className).toEqual('a b c');

removeClassNamesFromElement(element, classNames);

expect(element.className).toEqual('');
});
});
20 changes: 9 additions & 11 deletions packages/lexical-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
LexicalNode,
} from 'lexical';
import invariant from 'shared/invariant';
import normalizeClassNames from 'shared/normalizeClassNames';

export {default as markSelection} from './markSelection';
export {default as mergeRegister} from './mergeRegister';
Expand All @@ -49,12 +50,10 @@ export function addClassNamesToElement(
element: HTMLElement,
...classNames: Array<typeof undefined | boolean | null | string>
): void {
classNames.forEach((className) => {
if (typeof className === 'string') {
const classesToAdd = className.split(' ').filter((n) => n !== '');
element.classList.add(...classesToAdd);
}
});
const classesToAdd = normalizeClassNames(...classNames);
if (classesToAdd.length > 0) {
element.classList.add(...classesToAdd);
}
}

/**
Expand All @@ -69,11 +68,10 @@ export function removeClassNamesFromElement(
element: HTMLElement,
...classNames: Array<typeof undefined | boolean | null | string>
): void {
classNames.forEach((className) => {
if (typeof className === 'string') {
element.classList.remove(...className.split(' '));
}
});
const classesToRemove = normalizeClassNames(...classNames);
if (classesToRemove.length > 0) {
element.classList.remove(...classesToRemove);
}
}

/**
Expand Down
5 changes: 3 additions & 2 deletions packages/lexical/src/LexicalReconciler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {NodeKey, NodeMap} from './LexicalNode';
import type {ElementNode} from './nodes/LexicalElementNode';

import invariant from 'shared/invariant';
import normalizeClassNames from 'shared/normalizeClassNames';

import {
$isDecoratorNode,
Expand Down Expand Up @@ -368,7 +369,7 @@ function reconcileBlockDirection(element: ElementNode, dom: HTMLElement): void {
// Remove the old theme classes if they exist
if (previousDirectionTheme !== undefined) {
if (typeof previousDirectionTheme === 'string') {
const classNamesArr = previousDirectionTheme.split(' ');
const classNamesArr = normalizeClassNames(previousDirectionTheme);
previousDirectionTheme = theme[previousDirection] = classNamesArr;
}

Expand All @@ -386,7 +387,7 @@ function reconcileBlockDirection(element: ElementNode, dom: HTMLElement): void {
// Apply the new theme classes if they exist
if (nextDirectionTheme !== undefined) {
if (typeof nextDirectionTheme === 'string') {
const classNamesArr = nextDirectionTheme.split(' ');
const classNamesArr = normalizeClassNames(nextDirectionTheme);
// @ts-expect-error: intentional
nextDirectionTheme = theme[direction] = classNamesArr;
}
Expand Down
5 changes: 3 additions & 2 deletions packages/lexical/src/LexicalUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type {TextFormatType, TextNode} from './nodes/LexicalTextNode';
import {CAN_USE_DOM} from 'shared/canUseDOM';
import {IS_APPLE, IS_APPLE_WEBKIT, IS_IOS, IS_SAFARI} from 'shared/environment';
import invariant from 'shared/invariant';
import normalizeClassNames from 'shared/normalizeClassNames';

import {
$createTextNode,
Expand Down Expand Up @@ -1035,7 +1036,7 @@ export function getCachedClassNameArray(
// className tokens to an array that can be
// applied to classList.add()/remove().
if (typeof classNames === 'string') {
const classNamesArr = classNames.split(' ');
const classNamesArr = normalizeClassNames(classNames);
classNamesCache[classNameThemeType] = classNamesArr;
return classNamesArr;
}
Expand Down Expand Up @@ -1404,7 +1405,7 @@ function createBlockCursorElement(editorConfig: EditorConfig): HTMLDivElement {
let blockCursorTheme = theme.blockCursor;
if (blockCursorTheme !== undefined) {
if (typeof blockCursorTheme === 'string') {
const classNamesArr = blockCursorTheme.split(' ');
const classNamesArr = normalizeClassNames(blockCursorTheme);
// @ts-expect-error: intentional
blockCursorTheme = theme.blockCursor = classNamesArr;
}
Expand Down
21 changes: 21 additions & 0 deletions packages/shared/src/normalizeClassNames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

export default function normalizeClassNames(
...classNames: Array<typeof undefined | boolean | null | string>
): Array<string> {
const rval = [];
for (const className of classNames) {
if (className && typeof className === 'string') {
for (const [s] of className.matchAll(/\S+/g)) {
rval.push(s);
}
}
}
return rval;
}
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@
"packages/shared/src/simpleDiffWithCursor.ts"
],
"shared/invariant": ["./packages/shared/src/invariant.ts"],
"shared/normalizeClassNames": [
"./packages/shared/src/normalizeClassNames.ts"
],
"shared/warnOnlyOnce": ["./packages/shared/src/warnOnlyOnce.ts"],
"shared/environment": ["./packages/shared/src/environment.ts"],
"shared/useLayoutEffect": ["./packages/shared/src/useLayoutEffect.ts"]
Expand Down

0 comments on commit 57068e5

Please sign in to comment.