Skip to content

Commit d526ec8

Browse files
Merge pull request #347 from preactjs/hoc-integration
2 parents 2a2c550 + 8b39d0c commit d526ec8

File tree

20 files changed

+287
-199
lines changed

20 files changed

+287
-199
lines changed

src/adapter/adapter/filter.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ export interface RawFilterState {
66
fragment: boolean;
77
dom: boolean;
88
hoc?: boolean;
9+
root?: boolean;
910
};
1011
}
1112

12-
export type TypeFilterValue = "dom" | "fragment" | "hoc";
13+
export type TypeFilterValue = "dom" | "fragment" | "hoc" | "root";
1314

1415
export interface FilterState {
1516
regex: RegExp[];
@@ -30,15 +31,15 @@ export type Filter = RegexFilter | TypeFilter;
3031

3132
export const DEFAULT_FIlTERS: FilterState = {
3233
regex: [],
33-
// TODO: Add default hoc-filter
34-
type: new Set(["dom", "fragment"]),
34+
type: new Set(["dom", "fragment", "root", "hoc"]),
3535
};
3636

3737
export function parseFilters(raw: RawFilterState): FilterState {
3838
const type = new Set<TypeFilterValue>();
3939
if (raw.type.fragment) type.add("fragment");
4040
if (raw.type.dom) type.add("dom");
4141
if (raw.type.hoc) type.add("hoc");
42+
if (raw.type.root) type.add("root");
4243

4344
return {
4445
regex: raw.regex.filter(x => x.enabled).map(x => new RegExp(x.value, "gi")),

src/adapter/adapter/highlight.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ export function createHightlighter(
5454
const node = getNearestElement(first);
5555
const nodeEnd = last ? getNearestElement(last) : null;
5656
if (node != null) {
57-
const label = renderer.getDisplayName(vnode);
57+
let label = renderer.getDisplayName(vnode);
58+
59+
// Account for HOCs
60+
const lastOpenIdx = label.lastIndexOf("(");
61+
const firstCloseIdx = label.indexOf(")");
62+
if (lastOpenIdx > -1 && lastOpenIdx < firstCloseIdx) {
63+
label = label.slice(lastOpenIdx + 1, firstCloseIdx) || "Anonymous";
64+
}
5865

5966
let size = measureNode(node);
6067
if (nodeEnd !== null) {

src/adapter/shared/traverse.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,29 @@ function update<T extends SharedVNode>(
502502
}
503503
}
504504

505+
/**
506+
* Crawl upwards through potentially filtered vnodes until
507+
* we find a non-filtered node or reach the top of the tree
508+
*/
509+
function findClosestNonFilteredParent<T extends SharedVNode>(
510+
ids: IdMappingState<T>,
511+
helpers: PreactBindings<T>,
512+
vnode: T,
513+
) {
514+
let parentId = -1;
515+
let ancestor: T | null = helpers.getAncestor(vnode);
516+
while (ancestor !== null) {
517+
parentId = getVNodeId(ids, ancestor);
518+
if (parentId !== -1) {
519+
break;
520+
}
521+
522+
ancestor = helpers.getAncestor(ancestor);
523+
}
524+
525+
return parentId;
526+
}
527+
505528
export function createCommit<T extends SharedVNode>(
506529
ids: IdMappingState<T>,
507530
roots: Set<T>,
@@ -538,7 +561,7 @@ export function createCommit<T extends SharedVNode>(
538561
parentId = -1;
539562
roots.add(vnode);
540563
} else {
541-
parentId = getVNodeId(ids, helpers.getAncestor(vnode)!);
564+
parentId = findClosestNonFilteredParent(ids, helpers, vnode);
542565
}
543566

544567
if (isNew) {
@@ -575,7 +598,11 @@ export function createCommit<T extends SharedVNode>(
575598
);
576599
}
577600

578-
commit.rootId = getVNodeId(ids, vnode);
601+
let rootId = getVNodeId(ids, vnode);
602+
if (rootId === -1) {
603+
rootId = findClosestNonFilteredParent(ids, helpers, vnode);
604+
}
605+
commit.rootId = rootId;
579606

580607
return commit;
581608
}

src/shells/shared/panel/settings.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ export async function loadSettings(window: Window, store: Store) {
1818
"debugMode",
1919
"highlightUpdates",
2020
"componentFilters",
21-
"experimentalFilters",
2221
],
2322
res,
2423
);
@@ -31,7 +30,6 @@ export async function loadSettings(window: Window, store: Store) {
3130
store.profiler.captureRenderReasons.$ = !!settings.captureRenderReasons;
3231
store.profiler.highlightUpdates.$ = !!settings.highlightUpdates;
3332
store.debugMode.$ = !!settings.debugMode;
34-
store.filter.experimental.$ = !!settings.experimentalFilters;
3533
if (settings.componentFilters) {
3634
store.filter.restore(settings.componentFilters);
3735
}
@@ -81,13 +79,6 @@ export function storeDebugMode(enabled: boolean) {
8179
store({ debugMode: enabled });
8280
}
8381

84-
/**
85-
* Enables experimental filters
86-
*/
87-
export function storeExperimentalFilters(enabled: boolean) {
88-
store({ experimentalFilters: enabled });
89-
}
90-
9182
/**
9283
* Visualize updates on the page.
9384
*/

src/view/components/elements/TreeBar.tsx

Lines changed: 78 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -120,65 +120,77 @@ export function TreeBar() {
120120
);
121121
}
122122

123+
function FilterCheck({
124+
checked,
125+
label,
126+
onInput,
127+
}: {
128+
checked: boolean;
129+
onInput: (checked: boolean) => void;
130+
label: string;
131+
}) {
132+
return (
133+
<label class={s.filterRow}>
134+
<span class={s.filterCheck}>
135+
<input
136+
type="checkbox"
137+
checked={checked}
138+
onInput={e => onInput((e.target as any).checked)}
139+
/>
140+
{checked ? <CheckboxChecked /> : <CheckboxUnChecked />}
141+
</span>
142+
<span class={s.filterValue}>{label}</span>
143+
</label>
144+
);
145+
}
146+
123147
export function FilterPopup() {
124148
const store = useStore();
125-
const filters = useObserver(() => store.filter.filters.$);
126-
const filterDom = useObserver(() => store.filter.filterDom.$);
127-
const filterHoc = useObserver(() => store.filter.filterHoc.$);
128-
const filterFragment = useObserver(() => store.filter.filterFragment.$);
129-
const experimental = useObserver(() => store.filter.experimental.$);
149+
const [filterDom, setFilterDom] = useState(store.filter.filterDom.$);
150+
const [filterFragment, setFilterFragment] = useState(
151+
store.filter.filterFragment.$,
152+
);
153+
const [filterHoc, setFilterHoc] = useState(store.filter.filterHoc.$);
154+
const [filterRoot, setFilterRoot] = useState(store.filter.filterRoot.$);
155+
const [filters, setFilters] = useState(store.filter.filters.$);
130156

131157
return (
132158
<div class={s.filter} data-testid="filter-popup">
133159
<form
134160
onSubmit={e => {
135161
e.preventDefault();
162+
163+
store.filter.filterDom.$ = filterDom;
164+
store.filter.filterFragment.$ = filterFragment;
165+
store.filter.filterRoot.$ = filterRoot;
166+
store.filter.filterHoc.$ = filterHoc;
167+
168+
store.filter.filters.$ = filters;
169+
136170
store.filter.submit();
137171
}}
138172
>
139173
{/* Native filters */}
140-
<label class={s.filterRow}>
141-
<span class={s.filterCheck}>
142-
<input
143-
type="checkbox"
144-
checked={filterFragment}
145-
onInput={e =>
146-
store.filter.setEnabled("fragment", (e.target as any).checked)
147-
}
148-
/>
149-
{filterFragment ? <CheckboxChecked /> : <CheckboxUnChecked />}
150-
</span>
151-
<span class={s.filterValue}>Fragments</span>
152-
</label>
153-
{/* Remove when hoc-filter becomes stable */}
154-
{experimental && (
155-
<label class={s.filterRow}>
156-
<span class={s.filterCheck}>
157-
<input
158-
type="checkbox"
159-
checked={filterHoc}
160-
onInput={e =>
161-
store.filter.setEnabled("hoc", (e.target as any).checked)
162-
}
163-
/>
164-
{filterHoc ? <CheckboxChecked /> : <CheckboxUnChecked />}
165-
</span>
166-
<span class={s.filterValue}>HOC-Components</span>
167-
</label>
168-
)}
169-
<label class={s.filterRow}>
170-
<span class={s.filterCheck}>
171-
<input
172-
type="checkbox"
173-
checked={filterDom}
174-
onInput={e =>
175-
store.filter.setEnabled("dom", (e.target as any).checked)
176-
}
177-
/>
178-
{filterDom ? <CheckboxChecked /> : <CheckboxUnChecked />}
179-
</span>
180-
<span class={s.filterValue}>DOM nodes</span>
181-
</label>
174+
<FilterCheck
175+
label="Roots"
176+
onInput={checked => setFilterRoot(checked)}
177+
checked={filterRoot}
178+
/>
179+
<FilterCheck
180+
label="Fragments"
181+
onInput={checked => setFilterFragment(checked)}
182+
checked={filterFragment}
183+
/>
184+
<FilterCheck
185+
label="HOC-Components"
186+
onInput={checked => setFilterHoc(checked)}
187+
checked={filterHoc}
188+
/>
189+
<FilterCheck
190+
label="DOM nodes"
191+
onInput={checked => setFilterDom(checked)}
192+
checked={filterDom}
193+
/>
182194
{/* Custom user filters */}
183195
{filters.map((x, i) => {
184196
return (
@@ -188,7 +200,9 @@ export function FilterPopup() {
188200
type="checkbox"
189201
checked={x.enabled}
190202
onInput={e => {
191-
store.filter.setEnabled(x, (e.target as any).checked);
203+
const copy = [...filters];
204+
copy[i].enabled = (e.target as any).checked;
205+
setFilters(copy);
192206
}}
193207
/>
194208
{x.enabled ? <CheckboxChecked /> : <CheckboxUnChecked />}
@@ -199,16 +213,25 @@ export function FilterPopup() {
199213
type="text"
200214
placeholder="MyComponent"
201215
value={x.value}
202-
onInput={e =>
203-
store.filter.setValue(x, (e.target as any).value)
204-
}
216+
onInput={e => {
217+
const copy = [...filters];
218+
copy[i].value = (e.target as any).value;
219+
setFilters(copy);
220+
}}
205221
/>
206222
</span>
207223
<span class={s.removeWrapper}>
208224
<IconBtn
209225
title="Remove filter"
210226
styling="secondary"
211-
onClick={() => store.filter.remove(x)}
227+
onClick={() => {
228+
const idx = filters.indexOf(x);
229+
if (idx > -1) {
230+
const copy = [...filters];
231+
copy.splice(idx, 1);
232+
setFilters(copy);
233+
}
234+
}}
212235
>
213236
<Remove />
214237
</IconBtn>
@@ -222,7 +245,9 @@ export function FilterPopup() {
222245
styling="secondary"
223246
title="Add new filter"
224247
testId="add-filter"
225-
onClick={() => store.filter.add()}
248+
onClick={() =>
249+
setFilters([...filters, { enabled: false, value: "" }])
250+
}
226251
>
227252
<span class={s.filterCheck}>
228253
<AddCircle />

src/view/components/elements/TreeView.module.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,9 @@
198198
.hocs > * + * {
199199
margin-left: 0.25rem;
200200
}
201+
202+
.rootLabel {
203+
color: var(--color-key-value);
204+
display: inline-block;
205+
padding-left: .2rem;
206+
}

src/view/components/elements/TreeView.tsx

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -189,13 +189,17 @@ export function TreeItem(props: { key: any; id: ID; top: number }) {
189189
const store = useStore();
190190
const as = useSelection();
191191
const { collapsed, toggle } = useCollapser();
192-
const filterFragments = useObserver(() => store.filter.filterFragment.$);
193192
const node = useObserver(() => store.nodes.$.get(id) || null);
193+
const filterRoot = useObserver(() => store.filter.filterRoot.$);
194+
const filterHoc = useObserver(() => store.filter.filterHoc.$);
195+
const roots = useObserver(() => store.roots.$);
194196
const onToggle = () => toggle(id);
195197
const ref = useRef<HTMLDivElement>();
196198

197199
if (!node) return null;
198200

201+
const isRoot = node.parent === -1 && roots.includes(node.id);
202+
199203
return (
200204
<div
201205
ref={ref}
@@ -215,7 +219,7 @@ export function TreeItem(props: { key: any; id: ID; top: number }) {
215219
<div
216220
class={s.itemHeader}
217221
style={`transform: translate3d(calc(var(--indent-depth) * ${
218-
node.depth - (filterFragments ? 1 : 0)
222+
node.depth + (filterRoot ? -1 : 0)
219223
}), 0, 0);`}
220224
>
221225
{node.children.length > 0 && (
@@ -242,23 +246,36 @@ export function TreeItem(props: { key: any; id: ID; top: number }) {
242246
) : (
243247
""
244248
)}
245-
{node.hocs && node.hocs.length > 0 && (
246-
<span class={s.hocs}>
247-
{node.hocs.map((hoc, i) => {
248-
return (
249-
<Hoc key={i} small>
250-
<MarkResult text={hoc} id={id} />
251-
</Hoc>
252-
);
253-
})}
254-
</span>
249+
{filterHoc && node.hocs && node.hocs.length > 0 && (
250+
<HocLabels hocs={node.hocs} nodeId={id} />
255251
)}
252+
{isRoot ? <span class={s.rootLabel}>(Root)</span> : ""}
256253
</span>
257254
</div>
258255
</div>
259256
);
260257
}
261258

259+
export function HocLabels({
260+
hocs,
261+
nodeId,
262+
}: {
263+
hocs: string[];
264+
nodeId: number;
265+
}) {
266+
return (
267+
<span class={s.hocs} data-testid="hoc-labels">
268+
{hocs.map((hoc, i) => {
269+
return (
270+
<Hoc key={i} small>
271+
<MarkResult text={hoc} id={nodeId} />
272+
</Hoc>
273+
);
274+
})}
275+
</span>
276+
);
277+
}
278+
262279
export function Arrow() {
263280
return (
264281
<svg

0 commit comments

Comments
 (0)