✨ feat(vscode): React 钩子卡片按变量讲,属性表加操作列、调用树能查所有引用
- 钩子调用讲成状态、引用、记忆值、记忆函数、上下文值、过渡、副作用、钩子返回值、钩子调用几种卡片,行照签名表 reactHooks.ts 排,去掉 react.hook - 解构接住时名称一行写出解构,展开是「第几项 | 名称 | 说明」表,每项一个调用树操作;单个名字的按钮在名称一行最右边 - 属性表的行、子行可以带操作(RowAction),表格多一列「操作」,宿主按块记号加序号起键 - 调用树服务按键数次数,新增查所有引用(references):按所在函数分组,去掉定义处和依赖数组里的 - 钩子收的函数参数都收成小框,函数体是卡片子块,直接返回的表达式由 functionBody 排成「返回 …」卡片;小框和卡片折叠箭头同一个开关 - 规则卡片可指定语言种类图标(languageKind)、种类图标悬停字和颜色(KindBadge.tooltip / iconColor)
This commit is contained in:
1 parent
a139341132
commit
86beaac371
38 files changed
+1971
-813
No files matched your search
@@ -3,6 +3,8 @@
|
||||
import type { BlockTreeHandle, BlockTreeState, RewriteTree } from './BlockTree'
|
||||
import type { Ask, ChatState } from './qa'
|
||||
import type { BlockView, KindBadge } from './types'
|
||||
import { act } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { renderToStaticMarkup } from 'react-dom/server'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { fileAttributes } from '../../../src/codeReader/attributes'
|
||||
@@ -151,13 +153,64 @@ describe('路由筛选后展开函数体', () => {
|
||||
})
|
||||
|
||||
describe('库规则卡片的淡底', () => {
|
||||
it('卡片给了 tint 时淡底用它、描边仍是种类色(React 钩子卡片按来源分底色):两个颜色都写在卡片上', () => {
|
||||
const tinted: BlockView = { ...view('0', 'react.hook', 'useRouter'), badge: BADGE, tint: { dark: '#e5c07b', light: '#9a6a00' } }
|
||||
expect(markup([tinted], tree())).toContain('data-kind="react.hook" data-badge="" data-tint="" style="--kind-dark:#61dafb;--kind-light:#0a7ea4;--tint-dark:#e5c07b;--tint-light:#9a6a00"')
|
||||
it('卡片给了 tint 时淡底用它、描边仍是种类色(React 钩子返回值按来源分底色):两个颜色都写在卡片上', () => {
|
||||
const tinted: BlockView = { ...view('0', 'react.hookValue', 'useRouter'), badge: BADGE, tint: { dark: '#e5c07b', light: '#9a6a00' } }
|
||||
expect(markup([tinted], tree())).toContain('data-kind="react.hookValue" data-badge="" data-tint="" style="--kind-dark:#61dafb;--kind-light:#0a7ea4;--tint-dark:#e5c07b;--tint-light:#9a6a00"')
|
||||
expect(markup([{ ...tinted, tint: undefined }], tree())).not.toContain('data-tint')
|
||||
})
|
||||
})
|
||||
|
||||
describe('卡片里收起来的函数小框', () => {
|
||||
/** React 记忆值卡片:「计算」一行是收起来的函数,函数体(一张返回卡片)是卡片下面那一层的子块 */
|
||||
const memo = (children: BlockView[]): BlockView => ({
|
||||
...view('0', 'react.memo', ''),
|
||||
badge: BADGE,
|
||||
properties: [
|
||||
{ label: '名称', value: [{ text: 'visible', role: 'variable' }] },
|
||||
{ label: '计算', value: [{ style: 'fold', label: '函数', segments: [{ text: '· 1 句', role: 'hint' }] }] },
|
||||
],
|
||||
children,
|
||||
})
|
||||
const RETURN = view('0.0', 'return', '返回 results.filter(…)')
|
||||
const chip = (html: string): string => html.match(/<button[^>]*data-style="fold"[^>]*>/)?.[0] ?? ''
|
||||
|
||||
it('卡片有下面那一层的子块时,小框能点、和卡片的折叠箭头同开同关', () => {
|
||||
const closed = chip(markup([memo([RETURN])], tree({ collapsed: new Set(['0']) })))
|
||||
expect(closed).toContain('aria-expanded="false"')
|
||||
expect(closed).toContain('aria-label="展开函数"')
|
||||
expect(closed).not.toContain('disabled')
|
||||
const open = chip(markup([memo([RETURN])], tree({ collapsed: new Set() })))
|
||||
expect(open).toContain('aria-expanded="true"')
|
||||
expect(open).toContain('data-open=""')
|
||||
})
|
||||
|
||||
it('点小框切换的是卡片自己的折叠', async () => {
|
||||
const toggled: string[] = []
|
||||
const container = document.createElement('div')
|
||||
document.body.append(container)
|
||||
const root = createRoot(container)
|
||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
|
||||
await act(async () => root.render(<BlockTree blocks={[memo([RETURN])]} tree={{ ...tree({ collapsed: new Set(['0']) }), onToggleFold: id => toggled.push(id) }} />))
|
||||
await act(async () => container.querySelector<HTMLButtonElement>('[data-style="fold"]')!.click())
|
||||
expect(toggled).toEqual(['0'])
|
||||
await act(async () => root.unmount())
|
||||
container.remove()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
it('卡片没有子块、子块都挂在子行上时,小框只是不能点的小框,也不沿用外层卡片的开关', () => {
|
||||
expect(chip(markup([memo([])], tree()))).toContain('disabled')
|
||||
const slotted = view('0.0', 'demo.method', '', { slot: { row: 0, item: 0 } })
|
||||
expect(chip(markup([memo([slotted])], tree()))).toContain('disabled')
|
||||
// 挂在外层卡片子行上的子卡片画在外层卡片的属性表里,外层卡片自己有下面那一层的子块
|
||||
const inner: BlockView = { ...memo([]), id: '0.0', slot: { row: 1, item: 0 } }
|
||||
const outer: BlockView = { ...CARD, children: [inner, view('0.2', 'statement', '留下的语句')] }
|
||||
const html = markup([outer], tree({ collapsed: new Set() }))
|
||||
expect(html).toContain('计算')
|
||||
expect(chip(html)).toContain('disabled')
|
||||
})
|
||||
})
|
||||
|
||||
describe('挂在卡片子行上的子块', () => {
|
||||
it('默认不画,子行的名称是展开它的按钮;卡片收着时留下的子块不画', () => {
|
||||
const html = render({})
|
||||
|
||||
Reference in new issue
Block a user