• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

worktile / slate-angular / 8420d7ab-e60c-4fdf-8360-a6a2c427db7a

12 Nov 2025 03:31AM UTC coverage: 47.244% (+0.4%) from 46.877%
8420d7ab-e60c-4fdf-8360-a6a2c427db7a

Pull #300

circleci

pubuzhixing8
fix: unit test
Pull Request #300: feat(flavour): support extendable flavour(based dom) to improve performance

373 of 992 branches covered (37.6%)

Branch coverage included in aggregate %.

234 of 291 new or added lines in 17 files covered. (80.41%)

24 existing lines in 3 files now uncovered.

1007 of 1929 relevant lines covered (52.2%)

32.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.48
/packages/src/components/string/string-render.ts
1
import { SlateLeafContext } from '../../view/context';
2
import { SlateViewContext } from '../../view/context';
3
import { Text, Node } from 'slate';
4

5
export enum StringType {
1 ✔
6
    'normalString' = 'normalString',
1 ✔
7
    'lineBreakEmptyString' = 'lineBreakEmptyString',
1 ✔
8
    'normalEmptyText' = 'normalEmptyText',
1 ✔
9
    'compatibleString' = 'compatibleString',
1 ✔
10
    'voidString' = 'voidString'
1 ✔
11
}
12

13
export class SlateStringRender {
14
    nativeElement: HTMLElement;
15

16
    constructor(
17
        public context: SlateLeafContext,
152 ✔
18
        public viewContext: SlateViewContext
152 ✔
19
    ) {}
20

21
    type: StringType;
22

23
    // COMPAT: If the text is empty, it's because it's on the edge of an inline
24
    // node, so we render a zero-width space so that the selection can be
25
    // inserted next to it still.
26
    isEmptyText() {
27
        return this.context.leaf.text === '';
147 ✔
28
    }
29

30
    // COMPAT: Browsers will collapse trailing new lines at the end of blocks,
31
    // so we need to add an extra trailing new lines to prevent that.
32
    isCompatibleString() {
33
        return this.context.isLast && this.context.leaf.text.slice(-1) === '\n';
147 ✔
34
    }
35

36
    // COMPAT: Render text inside void nodes with a zero-width space.
37
    // So the node can contain selection but the text is not visible.
38
    isVoid() {
39
        return this.viewContext.editor.isVoid(this.context.parent);
148 ✔
40
    }
41

42
    get leaf(): Text {
43
        return this.context && this.context.leaf;
147 ✔
44
    }
45

46
    // COMPAT: If this is the last text node in an empty block, render a zero-
47
    // width space that will convert into a line break when copying and pasting
48
    // to support expected plain text.
49
    isLineBreakEmptyString() {
50
        return (
158 ✔
51
            this.context.leaf.text === '' &&
191 ✔
52
            this.context.parent.children[this.context.parent.children.length - 1] === this.context.text &&
53
            !this.viewContext.editor.isInline(this.context.parent) &&
54
            // [list-render] performance optimization: reduce the number of calls to the `Editor.string(editor, path)` method
55
            isEmpty(this.viewContext.editor, this.context.parent)
56
        );
57
    }
58

59
    createStringNode(type: StringType) {
60
        let newNativeElement: HTMLElement;
61
        switch (type) {
155 !
62
            case StringType.lineBreakEmptyString:
63
                newNativeElement = createLineBreakEmptyStringDOM(this.getElementStringLength());
8 ✔
64
                break;
8 ✔
65
            case StringType.voidString:
66
            case StringType.normalEmptyText:
67
                newNativeElement = createEmptyOrVoidStringNode();
1 ✔
68
                break;
1 ✔
69
            case StringType.compatibleString:
NEW
70
                newNativeElement = createCompatibleStringNode(this.leaf.text);
×
NEW
71
                break;
×
72
            case StringType.normalString:
73
                newNativeElement = createDefaultStringNode(this.leaf.text);
146 ✔
74
                break;
146 ✔
75
            default:
NEW
76
                newNativeElement = createDefaultStringNode(this.leaf.text);
×
77
        }
78
        return newNativeElement;
155 ✔
79
    }
80

81
    render() {
82
        this.type = this.getType();
152 ✔
83
        this.nativeElement = this.createStringNode(this.type);
152 ✔
84
        return this.nativeElement;
152 ✔
85
    }
86

87
    getType() {
88
        if (this.isLineBreakEmptyString()) {
158 ✔
89
            return StringType.lineBreakEmptyString;
10 ✔
90
        }
91
        if (this.isVoid()) {
148 ✔
92
            return StringType.voidString;
1 ✔
93
        }
94
        if (this.isEmptyText()) {
147 !
NEW
95
            return StringType.normalEmptyText;
×
96
        }
97
        if (this.isCompatibleString()) {
147 !
NEW
98
            return StringType.compatibleString;
×
99
        }
100
        return StringType.normalString;
147 ✔
101
    }
102

103
    update(context: SlateLeafContext, viewContext: SlateViewContext) {
104
        this.context = context;
6 ✔
105
        this.viewContext = viewContext;
6 ✔
106
        const type = this.getType();
6 ✔
107
        if (type !== this.type) {
6 ✔
108
            const newNativeElement = this.createStringNode(type);
3 ✔
109
            this.nativeElement.replaceWith(newNativeElement);
3 ✔
110
            this.nativeElement = newNativeElement;
3 ✔
111
            this.type = type;
3 ✔
112
            return;
3 ✔
113
        }
114
        if (this.type === StringType.normalString) {
3 ✔
115
            this.nativeElement.textContent = this.leaf.text;
1 ✔
116
        }
117
    }
118

119
    getElementStringLength() {
120
        return Node.string(this.context.parent).length;
8 ✔
121
    }
122
}
123

124
export const createDefaultStringNode = (text: string) => {
1 ✔
125
    const stringNode = document.createElement('span');
146 ✔
126
    stringNode.textContent = text;
146 ✔
127
    stringNode.setAttribute('data-slate-string', 'true');
146 ✔
128
    stringNode.setAttribute('editable-text', '');
146 ✔
129
    return stringNode;
146 ✔
130
};
131

132
export const createEmptyOrVoidStringNode = () => {
1 ✔
133
    const stringNode = document.createElement('span');
1 ✔
134
    stringNode.setAttribute('data-slate-string', 'true');
1 ✔
135
    stringNode.setAttribute('data-slate-zero-width', 'z');
1 ✔
136
    stringNode.setAttribute('data-slate-length', '0');
1 ✔
137
    const zeroWidthSpace = document.createTextNode('\uFEFF');
1 ✔
138
    stringNode.appendChild(zeroWidthSpace);
1 ✔
139
    stringNode.setAttribute('editable-text', '');
1 ✔
140
    return stringNode;
1 ✔
141
};
142

143
export const createCompatibleStringNode = (text: string) => {
1 ✔
NEW
144
    const stringNode = document.createElement('span');
×
NEW
145
    stringNode.setAttribute('data-slate-string', 'true');
×
NEW
146
    stringNode.textContent = text;
×
NEW
147
    stringNode.setAttribute('editable-text', '');
×
NEW
148
    const zeroWidthSpan = document.createElement('span');
×
NEW
149
    const zeroWidthSpace = document.createTextNode('\uFEFF');
×
NEW
150
    zeroWidthSpan.setAttribute('data-slate-zero-width', '');
×
NEW
151
    zeroWidthSpan.appendChild(zeroWidthSpace);
×
NEW
152
    stringNode.appendChild(zeroWidthSpan);
×
NEW
153
    return stringNode;
×
154
};
155

156
export const createLineBreakEmptyStringDOM = (elementStringLength: number) => {
1 ✔
157
    const stringNode = document.createElement('span');
8 ✔
158
    stringNode.setAttribute('data-slate-zero-width', 'n');
8 ✔
159
    stringNode.setAttribute('data-slate-length', `${elementStringLength}`);
8 ✔
160
    const zeroWidthSpace = document.createTextNode(`\uFEFF`);
8 ✔
161
    stringNode.appendChild(zeroWidthSpace);
8 ✔
162
    const brNode = document.createElement('br');
8 ✔
163
    stringNode.appendChild(brNode);
8 ✔
164
    stringNode.setAttribute('editable-text', '');
8 ✔
165
    return stringNode;
8 ✔
166
};
167

168
/**
169
 * TODO: remove when bump slate
170
 * copy from slate
171
 * @param editor
172
 * @param element
173
 * @returns
174
 */
175
export const isEmpty = (editor, element) => {
1 ✔
176
    const { children } = element;
11 ✔
177
    const [first] = children;
11 ✔
178
    return children.length === 0 || (children.length === 1 && Text.isText(first) && first.text === '' && !editor.isVoid(element));
11 ✔
179
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc