@estiva-app/ui 0.24.0 โ 0.24.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ListColumn.d.ts +41 -0
- package/dist/ListColumn.d.ts.map +1 -0
- package/dist/Select.d.ts +1 -0
- package/dist/Select.d.ts.map +1 -1
- package/dist/Toolbar.d.ts +18 -0
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -38
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/registry.json +164 -4
- package/src/AppShell.mdx +8 -1
- package/src/AttachmentCard.mdx +3 -1
- package/src/Avatar.mdx +7 -2
- package/src/Banner.mdx +4 -5
- package/src/Breadcrumb.mdx +2 -0
- package/src/Button.mdx +3 -2
- package/src/Button.tsx +2 -2
- package/src/Card.mdx +5 -1
- package/src/Checkbox.mdx +1 -0
- package/src/ChipInput.mdx +1 -1
- package/src/CollapsibleSection.mdx +3 -3
- package/src/ConfirmDialog.mdx +2 -2
- package/src/ContainerHeader.mdx +12 -3
- package/src/DialogShell.mdx +1 -1
- package/src/Divider.mdx +3 -0
- package/src/EditableText.mdx +6 -1
- package/src/EmptyState.mdx +2 -2
- package/src/Field.mdx +3 -4
- package/src/Form.mdx +1 -1
- package/src/IconButton.mdx +1 -1
- package/src/IdentityMenu.mdx +1 -1
- package/src/InlineChip.mdx +3 -0
- package/src/Kbd.mdx +2 -2
- package/src/Link.mdx +3 -1
- package/src/ListColumn.mdx +87 -0
- package/src/ListColumn.stories.tsx +136 -0
- package/src/ListColumn.test.tsx +50 -0
- package/src/ListColumn.tsx +63 -0
- package/src/Menu.mdx +2 -0
- package/src/MenuItem.mdx +3 -0
- package/src/NavItem.mdx +4 -0
- package/src/Person.mdx +3 -0
- package/src/PersonTrigger.mdx +2 -1
- package/src/PersonTrigger.tsx +1 -1
- package/src/Popover.mdx +2 -0
- package/src/Rail.mdx +3 -0
- package/src/RailItem.mdx +4 -0
- package/src/RailItem.tsx +1 -1
- package/src/Reaction.mdx +1 -0
- package/src/Reaction.tsx +1 -1
- package/src/ScrollArea.mdx +3 -0
- package/src/SectionHeader.mdx +5 -1
- package/src/SectionHeader.tsx +1 -1
- package/src/SectionLabel.mdx +6 -2
- package/src/Select.mdx +3 -2
- package/src/Select.test.tsx +12 -0
- package/src/Select.tsx +4 -3
- package/src/Skeleton.mdx +5 -1
- package/src/Skeleton.tsx +1 -1
- package/src/TextInput.mdx +1 -0
- package/src/TextInput.tsx +1 -1
- package/src/Toolbar.mdx +9 -2
- package/src/Toolbar.stories.tsx +19 -2
- package/src/Toolbar.test.tsx +24 -1
- package/src/Toolbar.tsx +22 -0
- package/src/Tooltip.mdx +2 -1
- package/src/Tooltip.test.tsx +22 -0
- package/src/Tooltip.tsx +1 -1
- package/src/TopBar.mdx +8 -1
- package/src/heights.test.tsx +78 -0
- package/src/index.ts +2 -1
- package/src/registry/registry.test.ts +16 -8
package/src/Skeleton.tsx
CHANGED
|
@@ -21,7 +21,7 @@ const ROW_BAR_WIDTHS = [150, 100, 170, 120, 90, 140, 110, 160]
|
|
|
21
21
|
/** A 32px row: a 16px square and a bar, like a row with a face and a name. */
|
|
22
22
|
export function SkeletonRow({ barWidth = 130 }: { barWidth?: number }) {
|
|
23
23
|
return (
|
|
24
|
-
<div className="flex items-center gap-2 px-2 h-[32px] rounded-lg">
|
|
24
|
+
<div className="flex shrink-0 items-center gap-2 px-2 h-[32px] rounded-lg">
|
|
25
25
|
<SkeletonBar className="w-4 h-4 shrink-0" />
|
|
26
26
|
<SkeletonBar className="h-3.5" style={{ width: barWidth }} />
|
|
27
27
|
</div>
|
package/src/TextInput.mdx
CHANGED
|
@@ -53,6 +53,7 @@ It does these by itself. Write none of them around it.
|
|
|
53
53
|
| It does | So you never write | Refused when written by hand |
|
|
54
54
|
|---|---|---|
|
|
55
55
|
| Inside a `Field`, takes its name from the Field's label, and its error state | an `id` / `htmlFor` pair, or `aria-invalid` | |
|
|
56
|
+
| Keeps its height in a scrolling column | `shrink-0` | |
|
|
56
57
|
|
|
57
58
|
## Props
|
|
58
59
|
|
package/src/TextInput.tsx
CHANGED
|
@@ -38,7 +38,7 @@ export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function T
|
|
|
38
38
|
'bg-bg-inset border border-border-default hover:border-border-strong focus:border-border-focus rounded-lg',
|
|
39
39
|
// The small size is the small Select's trigger, class for class.
|
|
40
40
|
size === 'default' && 'px-3 py-2 text-input-value',
|
|
41
|
-
size === 'small' && 'h-6 px-2 text-caption',
|
|
41
|
+
size === 'small' && 'h-6 min-h-6 px-2 text-caption',
|
|
42
42
|
'text-text-primary placeholder:text-text-muted',
|
|
43
43
|
'outline-none transition-colors',
|
|
44
44
|
'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled',
|
package/src/Toolbar.mdx
CHANGED
|
@@ -69,8 +69,8 @@ import { Toolbar, ToolbarButton, ToolbarSeparator } from '@estiva-app/ui'
|
|
|
69
69
|
A toolbar floats over what it acts on โ a card, a paragraph, an image โ so it
|
|
70
70
|
draws the elevated surface that separates it from that: **the same `MenuPanel`
|
|
71
71
|
a `Menu` draws**, because a floating strip and a floating list are the same
|
|
72
|
-
box.
|
|
73
|
-
|
|
72
|
+
box. Built twice by hand, the two drift: one `rounded-sm` with `shadow-sm`,
|
|
73
|
+
the other `rounded-lg` with `shadow-lg`.
|
|
74
74
|
|
|
75
75
|
`surface={false}` for a strip inside something that draws that box already โ a
|
|
76
76
|
`Popover`, a dialog, a card's own panel.
|
|
@@ -82,6 +82,13 @@ why the placement is its job and not arithmetic of ours.
|
|
|
82
82
|
|
|
83
83
|
<Canvas of={ToolbarStories.OnAnExistingSurface} />
|
|
84
84
|
|
|
85
|
+
**A control that goes somewhere is a `ToolbarLink`** with an `href`: the
|
|
86
|
+
strip's look, in the same walk, with a real address, so a Ctrl+click opens a
|
|
87
|
+
new tab. A `ToolbarButton` cannot be a link. A place you cannot go is left out
|
|
88
|
+
of the strip rather than disabled.
|
|
89
|
+
|
|
90
|
+
<Canvas of={ToolbarStories.WithALink} />
|
|
91
|
+
|
|
85
92
|
## What it owns
|
|
86
93
|
|
|
87
94
|
It does these by itself. Write none of them around it.
|
package/src/Toolbar.stories.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
-
import { IconArrowBackUp, IconArrowForwardUp, IconBold, IconItalic, IconLink, IconSquareRounded, IconUnderline } from '@tabler/icons-react'
|
|
2
|
+
import { IconArrowBackUp, IconArrowForwardUp, IconArrowUpRight, IconBold, IconItalic, IconLink, IconSquareRounded, IconUnderline } from '@tabler/icons-react'
|
|
3
3
|
import { useState } from 'react'
|
|
4
4
|
import { IconButton } from './IconButton'
|
|
5
5
|
import { Popover } from './Popover'
|
|
6
|
-
import { Toolbar, ToolbarButton, ToolbarInput, ToolbarSeparator } from './Toolbar'
|
|
6
|
+
import { Toolbar, ToolbarButton, ToolbarInput, ToolbarLink, ToolbarSeparator } from './Toolbar'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* A strip of controls that behaves as **one** control: Tab in, arrow keys
|
|
@@ -100,6 +100,23 @@ export const WithADisabledControl: Story = {
|
|
|
100
100
|
),
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* A control that goes somewhere, beside controls that do something. The link
|
|
105
|
+
* has the buttons' look and joins the same walk; it has a real address, so a
|
|
106
|
+
* Ctrl+click opens a new tab.
|
|
107
|
+
*/
|
|
108
|
+
export const WithALink: Story = {
|
|
109
|
+
parameters: { controls: { disable: true } },
|
|
110
|
+
render: () => (
|
|
111
|
+
<Toolbar aria-label="Item actions" surface={false}>
|
|
112
|
+
<ToolbarButton aria-label="Item one" tooltip="Item one">{icon}</ToolbarButton>
|
|
113
|
+
<ToolbarLink href="#item" aria-label="Open the item" tooltip="Open">
|
|
114
|
+
<IconArrowUpRight size={16} stroke={1.5} />
|
|
115
|
+
</ToolbarLink>
|
|
116
|
+
</Toolbar>
|
|
117
|
+
),
|
|
118
|
+
}
|
|
119
|
+
|
|
103
120
|
/** Down rather than across: โ and โ walk it. */
|
|
104
121
|
export const Vertical: Story = {
|
|
105
122
|
parameters: { controls: { disable: true } },
|
package/src/Toolbar.test.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
|
7
7
|
import { cleanup, render, screen } from '@testing-library/react'
|
|
8
8
|
import userEvent from '@testing-library/user-event'
|
|
9
9
|
import { IconButton } from './IconButton'
|
|
10
|
-
import { Toolbar, ToolbarButton, ToolbarInput, ToolbarSeparator } from './Toolbar'
|
|
10
|
+
import { Toolbar, ToolbarButton, ToolbarInput, ToolbarLink, ToolbarSeparator } from './Toolbar'
|
|
11
11
|
import { Menu, MenuItem } from './Menu'
|
|
12
12
|
import { Popover } from './Popover'
|
|
13
13
|
|
|
@@ -176,6 +176,29 @@ describe('Toolbar', () => {
|
|
|
176
176
|
})
|
|
177
177
|
})
|
|
178
178
|
|
|
179
|
+
describe('a link in the strip (F3)', () => {
|
|
180
|
+
it('is a real link, in the walk with the buttons beside it', async () => {
|
|
181
|
+
const user = userEvent.setup()
|
|
182
|
+
render(
|
|
183
|
+
<>
|
|
184
|
+
<button type="button">Before</button>
|
|
185
|
+
<Toolbar aria-label="Item actions">
|
|
186
|
+
<ToolbarButton aria-label="One">{dot}</ToolbarButton>
|
|
187
|
+
<ToolbarLink href="/somewhere" aria-label="Open" tooltip="Open">{dot}</ToolbarLink>
|
|
188
|
+
</Toolbar>
|
|
189
|
+
</>,
|
|
190
|
+
)
|
|
191
|
+
const link = screen.getByRole('link', { name: 'Open' })
|
|
192
|
+
expect(link.getAttribute('href')).toBe('/somewhere')
|
|
193
|
+
await user.click(screen.getByRole('button', { name: 'Before' }))
|
|
194
|
+
await user.tab()
|
|
195
|
+
expect(focused()).toBe('One')
|
|
196
|
+
await user.keyboard('{ArrowRight}')
|
|
197
|
+
expect(document.activeElement).toBe(link)
|
|
198
|
+
expect((await screen.findByRole('tooltip')).textContent).toBe('Open')
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
179
202
|
describe('a loose row, for comparison', () => {
|
|
180
203
|
/** The measurement the component exists to change: the same three buttons
|
|
181
204
|
* outside a toolbar are three Tab stops. */
|
package/src/Toolbar.tsx
CHANGED
|
@@ -129,6 +129,28 @@ export function ToolbarButton({ ref, disabled, disabledReason, ...props }: Toolb
|
|
|
129
129
|
)
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* A link in the strip: the strip's own look, and it goes somewhere. It has a
|
|
134
|
+
* real address, so a Ctrl+click or a middle click opens a new tab, which a
|
|
135
|
+
* button that navigates cannot do.
|
|
136
|
+
*
|
|
137
|
+
* It is `IconButton`'s `href` joined to the toolbar's walk: Base UI's
|
|
138
|
+
* `Toolbar.Link`. A `ToolbarButton` cannot be a link โ Base UI expects a real
|
|
139
|
+
* button there and warns when it gets an anchor (UIG-14, finding F3).
|
|
140
|
+
*
|
|
141
|
+
* A link is never disabled: a place you cannot go is left out of the strip.
|
|
142
|
+
*/
|
|
143
|
+
export interface ToolbarLinkProps extends Omit<IconButtonProps, 'href' | 'disabled' | 'disabledReason' | 'pressed' | 'type' | 'ref'> {
|
|
144
|
+
href: string
|
|
145
|
+
ref?: Ref<HTMLAnchorElement>
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** An `IconButton` that is a link, and an item of a `Toolbar`: the arrow keys
|
|
149
|
+
* reach it like the buttons beside it. */
|
|
150
|
+
export function ToolbarLink({ ref, href, ...props }: ToolbarLinkProps) {
|
|
151
|
+
return <BaseToolbar.Link ref={ref} render={<IconButton href={href} {...props} />} />
|
|
152
|
+
}
|
|
153
|
+
|
|
132
154
|
/**
|
|
133
155
|
* A text field inside the strip โ the link editor in a selection toolbar.
|
|
134
156
|
*
|
package/src/Tooltip.mdx
CHANGED
|
@@ -52,7 +52,8 @@ import { WithTooltip } from '@estiva-app/ui'
|
|
|
52
52
|
`wrapperClassName="min-w-0 shrink"`.
|
|
53
53
|
- **`inline`** for a trigger inside a line of text โ a reference, a name in a
|
|
54
54
|
sentence: the wrapper is a `<span>`, because a `<div>` is not allowed inside
|
|
55
|
-
a paragraph.
|
|
55
|
+
a paragraph. It sits at the top of the line by itself, so the line keeps its
|
|
56
|
+
height; add no wrapper of your own around it.
|
|
56
57
|
- A label longer than 320px wraps onto more lines rather than running off the
|
|
57
58
|
screen; a short one stays one line at 30px.
|
|
58
59
|
- **`Button` and `IconButton` need no wrapper**: pass `tooltip` (or
|
package/src/Tooltip.test.tsx
CHANGED
|
@@ -78,6 +78,28 @@ describe('WithTooltip', () => {
|
|
|
78
78
|
expect(errors).toEqual([])
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
+
// F1: without `align-top` a chip in the wrapper drops 0.2px and its line grows
|
|
82
|
+
// from 19.6 to 20px; the apps may not add the class to the part, so it carries it.
|
|
83
|
+
it('inline: the wrapper sits at the top of the line by itself', () => {
|
|
84
|
+
render(
|
|
85
|
+
<p>
|
|
86
|
+
<WithTooltip inline label="Item one">
|
|
87
|
+
<span>Item one</span>
|
|
88
|
+
</WithTooltip>
|
|
89
|
+
</p>,
|
|
90
|
+
)
|
|
91
|
+
expect(screen.getByText('Item one').parentElement?.className.split(' ')).toContain('align-top')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('a block wrapper is not moved', () => {
|
|
95
|
+
render(
|
|
96
|
+
<WithTooltip label="Item one">
|
|
97
|
+
<span>Item one</span>
|
|
98
|
+
</WithTooltip>,
|
|
99
|
+
)
|
|
100
|
+
expect(screen.getByText('Item one').parentElement?.className.split(' ')).not.toContain('align-top')
|
|
101
|
+
})
|
|
102
|
+
|
|
81
103
|
it('takes wrapperClassName, which is what keeps a truncating label truncating', () => {
|
|
82
104
|
const { container } = render(
|
|
83
105
|
<WithTooltip label="A long name" wrapperClassName="min-w-0 shrink">
|
package/src/Tooltip.tsx
CHANGED
|
@@ -171,7 +171,7 @@ export function WithTooltip({ label, shortcut, placement = 'top', wrapperClassNa
|
|
|
171
171
|
can carry the handlers for all of them. A control that IS the trigger
|
|
172
172
|
(Button, IconButton) composes the part onto itself instead, which is
|
|
173
173
|
what lets a `Dialog.Close` or a `Menu.Trigger` be one of those. */}
|
|
174
|
-
<BaseTooltip.Trigger delay={OPEN_DELAY} render={<Wrapper className={cn('inline-flex shrink-0', wrapperClassName)} />}>
|
|
174
|
+
<BaseTooltip.Trigger delay={OPEN_DELAY} render={<Wrapper className={cn('inline-flex shrink-0', inline && 'align-top', wrapperClassName)} />}>
|
|
175
175
|
{children}
|
|
176
176
|
</BaseTooltip.Trigger>
|
|
177
177
|
<TooltipSurface label={label} shortcut={shortcut} placement={placement} />
|
package/src/TopBar.mdx
CHANGED
|
@@ -57,9 +57,16 @@ import { TopBar, IdentityMenu, SearchInput } from '@estiva-app/ui'
|
|
|
57
57
|
- The frame is desktop-only for now โ there is no narrow-screen
|
|
58
58
|
behaviour; the menu button collapses panels, it does not open a drawer.
|
|
59
59
|
|
|
60
|
+
- The bar is 52px tall, 20px in on the left and 26px on the right, 16px between
|
|
61
|
+
its slots.
|
|
62
|
+
|
|
60
63
|
## What it owns
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
It does these by itself. Write none of them around it.
|
|
66
|
+
|
|
67
|
+
| It does | So you never write | Refused when written by hand |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| Keeps its 52px in a scrolling column | `shrink-0` | |
|
|
63
70
|
|
|
64
71
|
## Props
|
|
65
72
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* A part with a set height keeps it in a scrolling column (UIG-15; UIG-14 cards
|
|
4
|
+
* T1, M4, M6). A scrolling flex column squashes any child that may shrink:
|
|
5
|
+
* measured in Chrome, a 32px SectionHeader went to 24px, a Skeleton row to 18px,
|
|
6
|
+
* a Button to 18px. jsdom draws nothing, so this pins the class that prevents it;
|
|
7
|
+
* the measurement is in the record.
|
|
8
|
+
*
|
|
9
|
+
* Rows that span their column refuse to shrink (`shrink-0`). Controls, which
|
|
10
|
+
* mostly sit in rows, keep a minimum height instead: `shrink-0` would also stop
|
|
11
|
+
* a row from narrowing them.
|
|
12
|
+
*/
|
|
13
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
14
|
+
import { cleanup, render, screen } from '@testing-library/react'
|
|
15
|
+
import { Button } from './Button'
|
|
16
|
+
import { PersonTrigger } from './PersonTrigger'
|
|
17
|
+
import { RailItem } from './RailItem'
|
|
18
|
+
import { Reaction } from './Reaction'
|
|
19
|
+
import { SectionHeader } from './SectionHeader'
|
|
20
|
+
import { Select } from './Select'
|
|
21
|
+
import { SkeletonRow } from './Skeleton'
|
|
22
|
+
import { TextInput } from './TextInput'
|
|
23
|
+
|
|
24
|
+
afterEach(cleanup)
|
|
25
|
+
|
|
26
|
+
const classes = (el: Element | null) => (el?.getAttribute('class') ?? '').split(' ')
|
|
27
|
+
|
|
28
|
+
describe('a part with a set height keeps it in a scrolling column', () => {
|
|
29
|
+
it('SectionHeader: shrink-0', () => {
|
|
30
|
+
const { container } = render(<SectionHeader title="Group" />)
|
|
31
|
+
expect(classes(container.querySelector('[class*="h-[32px]"]'))).toContain('shrink-0')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('SkeletonRow: shrink-0', () => {
|
|
35
|
+
const { container } = render(<SkeletonRow />)
|
|
36
|
+
expect(classes(container.firstElementChild)).toContain('shrink-0')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('RailItem: a set 48px, and shrink-0', () => {
|
|
40
|
+
render(<RailItem href="#" label="Item" icon={<span />} />)
|
|
41
|
+
const tile = classes(screen.getByRole('link'))
|
|
42
|
+
expect(tile).toContain('h-12')
|
|
43
|
+
expect(tile).toContain('shrink-0')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('Button: a minimum height at each size', () => {
|
|
47
|
+
render(
|
|
48
|
+
<>
|
|
49
|
+
<Button>Default</Button>
|
|
50
|
+
<Button size="small">Small</Button>
|
|
51
|
+
</>,
|
|
52
|
+
)
|
|
53
|
+
expect(classes(screen.getByRole('button', { name: 'Default' }))).toContain('min-h-8')
|
|
54
|
+
expect(classes(screen.getByRole('button', { name: 'Small' }))).toContain('min-h-6')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('Select and TextInput, small: a minimum height', () => {
|
|
58
|
+
render(
|
|
59
|
+
<>
|
|
60
|
+
<Select size="small" value="a" onChange={() => {}} options={[{ value: 'a', label: 'A' }]} ariaLabel="Pick" />
|
|
61
|
+
<TextInput size="small" aria-label="Type" />
|
|
62
|
+
</>,
|
|
63
|
+
)
|
|
64
|
+
expect(classes(screen.getByRole('combobox', { name: 'Pick' }))).toContain('min-h-6')
|
|
65
|
+
expect(classes(screen.getByRole('textbox', { name: 'Type' }))).toContain('min-h-6')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('PersonTrigger and Reaction: a minimum height', () => {
|
|
69
|
+
render(
|
|
70
|
+
<>
|
|
71
|
+
<PersonTrigger name="Ana Duarte" />
|
|
72
|
+
<Reaction emoji="๐" count={2} aria-label="Thumbs up" />
|
|
73
|
+
</>,
|
|
74
|
+
)
|
|
75
|
+
expect(classes(screen.getByRole('button', { name: /Ana Duarte/ }))).toContain('min-h-8')
|
|
76
|
+
expect(classes(screen.getByRole('button', { name: 'Thumbs up' }))).toContain('min-h-6')
|
|
77
|
+
})
|
|
78
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -68,7 +68,7 @@ export { NavItem, type NavItemProps } from './NavItem'
|
|
|
68
68
|
export { Popover, type PopoverProps } from './Popover'
|
|
69
69
|
export { ScrollArea, type ScrollAreaProps } from './ScrollArea'
|
|
70
70
|
export { ReactionPicker, type ReactionOption, type ReactionPickerProps } from './ReactionPicker'
|
|
71
|
-
export { Toolbar, ToolbarButton, ToolbarInput, ToolbarSeparator, type ToolbarProps, type ToolbarButtonProps, type ToolbarInputProps } from './Toolbar'
|
|
71
|
+
export { Toolbar, ToolbarButton, ToolbarInput, ToolbarLink, ToolbarSeparator, type ToolbarProps, type ToolbarButtonProps, type ToolbarInputProps, type ToolbarLinkProps } from './Toolbar'
|
|
72
72
|
export { PreviewCard, type PreviewCardProps } from './PreviewCard'
|
|
73
73
|
export { Person, type PersonProps } from './Person'
|
|
74
74
|
export { Rail, type RailProps } from './Rail'
|
|
@@ -81,6 +81,7 @@ export { Reaction, type ReactionProps } from './Reaction'
|
|
|
81
81
|
export { SearchInput, type SearchInputProps } from './SearchInput'
|
|
82
82
|
export { SectionHeader, type SectionAction, type SectionHeaderProps } from './SectionHeader'
|
|
83
83
|
export { ContainerHeader, type ContainerHeaderProps } from './ContainerHeader'
|
|
84
|
+
export { ListColumn, type ListColumnProps } from './ListColumn'
|
|
84
85
|
export { CollapsibleSection, type CollapsibleSectionProps } from './CollapsibleSection'
|
|
85
86
|
export { SectionLabel } from './SectionLabel'
|
|
86
87
|
export { Tabs, type TabDef, type TabsProps } from './Tabs'
|
|
@@ -45,17 +45,17 @@ describe('the registry builds', () => {
|
|
|
45
45
|
expect(registry.entries.map((one) => one.name).sort()).toEqual(values.map((one) => one.name).sort())
|
|
46
46
|
})
|
|
47
47
|
|
|
48
|
-
it('counts
|
|
48
|
+
it('counts 84 names over 55 files: 77 components, 6 helpers and 1 hook (ListColumn and ToolbarLink joined in UIG-14)', () => {
|
|
49
49
|
// The reconciliation GATES.md ยง24 explains. If this changes, that changes.
|
|
50
50
|
const kinds = registry.entries.reduce<Record<string, number>>((all, one) => ({ ...all, [one.kind]: (all[one.kind] ?? 0) + 1 }), {})
|
|
51
|
-
expect(kinds).toEqual({ component:
|
|
52
|
-
expect(new Set(registry.entries.map((one) => one.sourceFile)).size).toBe(
|
|
51
|
+
expect(kinds).toEqual({ component: 77, helper: 6, hook: 1 })
|
|
52
|
+
expect(new Set(registry.entries.map((one) => one.sourceFile)).size).toBe(55)
|
|
53
53
|
})
|
|
54
54
|
|
|
55
55
|
it('gives every entry a purpose, from its own page or from the comment above it', () => {
|
|
56
56
|
expect(registry.entries.filter((one) => one.purpose.trim() === '')).toEqual([])
|
|
57
|
-
expect(registry.entries.filter((one) => one.purposeFrom === 'page').length).toBe(
|
|
58
|
-
expect(registry.entries.filter((one) => one.purposeFrom === 'comment').length).toBe(
|
|
57
|
+
expect(registry.entries.filter((one) => one.purposeFrom === 'page').length).toBe(55)
|
|
58
|
+
expect(registry.entries.filter((one) => one.purposeFrom === 'comment').length).toBe(29)
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
it('takes a name with a page of its own from the page, and a name without one from the code', () => {
|
|
@@ -278,7 +278,7 @@ describe('the ids point at something', () => {
|
|
|
278
278
|
describe('a second parser agrees', async () => {
|
|
279
279
|
const docgen = await import('react-docgen').catch(() => null)
|
|
280
280
|
|
|
281
|
-
it.skipIf(!docgen)('finds the same props on all
|
|
281
|
+
it.skipIf(!docgen)('finds the same props on all 77 components', async () => {
|
|
282
282
|
if (!docgen) return
|
|
283
283
|
const resolver = new docgen.builtinResolvers.FindExportedDefinitionsResolver({ limit: 0 })
|
|
284
284
|
const byName = new Map(registry.entries.map((one) => [one.name, one]))
|
|
@@ -321,12 +321,14 @@ describe('a second parser agrees', async () => {
|
|
|
321
321
|
const INHERITED_WITH_A_DEFAULT = new Set(['Button.type', 'IconButton.type', 'TextInput.type', 'SearchInput.placeholder'])
|
|
322
322
|
|
|
323
323
|
/**
|
|
324
|
-
* The
|
|
324
|
+
* The eighteen where **this registry is right and `react-docgen` is not**.
|
|
325
325
|
*
|
|
326
326
|
* `IdentityPanelProps extends Omit<IdentityMenuProps, 'compact'>` and
|
|
327
327
|
* `PersonTriggerProps extends Omit<PersonProps, 'className'>`: docgen does not
|
|
328
328
|
* follow a heritage clause wrapped in `Omit`, so it reports neither `me` nor
|
|
329
|
-
* `signedIn`, which `IdentityPanel` *requires*.
|
|
329
|
+
* `signedIn`, which `IdentityPanel` *requires*. `ToolbarLinkProps extends
|
|
330
|
+
* Omit<IconButtonProps, โฆ>` (UIG-14, F3) is the same: docgen reports none of
|
|
331
|
+
* the IconButton props it keeps. Checked against the source โ
|
|
330
332
|
* `IdentityMenuProps` declares both, and `IdentityPanel` spreads `...rest`
|
|
331
333
|
* straight into `IdentityRows`, which destructures them.
|
|
332
334
|
*
|
|
@@ -346,6 +348,12 @@ const DOCGEN_STOPS_AT_OMIT = new Set([
|
|
|
346
348
|
'PersonTrigger.picture',
|
|
347
349
|
'PersonTrigger.fallback',
|
|
348
350
|
'PersonTrigger.size',
|
|
351
|
+
'ToolbarLink.variant',
|
|
352
|
+
'ToolbarLink.glow',
|
|
353
|
+
'ToolbarLink.tooltip',
|
|
354
|
+
'ToolbarLink.tooltipShortcut',
|
|
355
|
+
'ToolbarLink.tooltipPlacement',
|
|
356
|
+
'ToolbarLink.children',
|
|
349
357
|
])
|
|
350
358
|
|
|
351
359
|
describe('ui:find', () => {
|