Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/app/src/components/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export class DevtoolsSidebar extends Element {
flex-direction: column;
height: 100%;
min-height: 0;
/* Without this, single-line test names make the automatic minimum
width beat the drag handle's flex-basis and the panel outgrows it. */
min-width: 0;
overflow: hidden;
}

.top {
Expand Down
10 changes: 9 additions & 1 deletion packages/app/src/components/sidebar/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class DevtoolsSidebarExplorer extends CollapseableEntry {
#statusFilter: TestStatus | null = null
#selectedUid?: string
#autoSelectedUid?: string
#revealedUid?: string
#filterListener = this.#filterTests.bind(this)
#statusFilterListener = this.#applyStatusFilter.bind(this)
#selectListener = this.#handleSelect.bind(this)
Expand All @@ -65,6 +66,7 @@ export class DevtoolsSidebarExplorer extends CollapseableEntry {
display: flex;
flex-direction: column;
min-height: 0;
min-width: 0;
flex: 1 1 auto;
}

Expand Down Expand Up @@ -140,7 +142,12 @@ export class DevtoolsSidebarExplorer extends CollapseableEntry {
}

#handleSelect(event: CustomEvent<string>) {
this.#selectedUid = event.detail
const uid = event.detail
// Only one row shows its full name at a time: clicking a row folds whichever
// was open, and clicking the open one folds it. The event is click-only, so
// the tree auto-selecting the running test never expands anything.
this.#revealedUid = uid === this.#revealedUid ? undefined : uid
this.#selectedUid = uid
this.requestUpdate()
}

Expand Down Expand Up @@ -375,6 +382,7 @@ export class DevtoolsSidebarExplorer extends CollapseableEntry {
?has-children="${entry.children && entry.children.length > 0}"
?selected="${entry.uid ===
(this.#selectedUid ?? this.#autoSelectedUid)}"
?revealed="${entry.uid === this.#revealedUid}"
?root="${isRoot}"
.runDisabled=${this.#isRunDisabled(entry)}
.runDisabledReason=${this.#getRunDisabledReason(entry)}
Expand Down
22 changes: 20 additions & 2 deletions packages/app/src/components/sidebar/test-suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ export class ExplorerTestEntry extends CollapseableEntry {
@property({ type: Boolean, reflect: true })
selected = false

/** Whether this row shows its name in full. Owned by the explorer, which keeps
* exactly one row revealed; `selected` can't drive it because the tree
* auto-selects the running test, expanding a row nobody touched. */
@property({ type: Boolean, reflect: true })
revealed = false

@property({ type: Boolean, reflect: true })
root = false

Expand All @@ -110,6 +116,18 @@ export class ExplorerTestEntry extends CollapseableEntry {
font-size: 12.5px;
/* matches the icon box height so the icon aligns with the first line */
line-height: 18px;
/* One line per row so every entry in the tree is the same height */
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Clicking a row is the only way to read a name it had to clip. Wrapping
anywhere because a name can be one unbroken token (a URL) that plain
wrapping would still clip. */
:host([revealed]) ::slotted(label) {
white-space: normal;
overflow-wrap: anywhere;
}

:host([selected]) .row {
Expand Down Expand Up @@ -426,13 +444,13 @@ export class ExplorerTestEntry extends CollapseableEntry {
></icon-mdi-menu-down>
</button>
<span
class="flex items-start shrink flex-nowrap min-w-0 leading-[18px] ${hasNoChildren
class="flex items-start flex-nowrap min-w-0 flex-1 leading-[18px] ${hasNoChildren
? 'pl-9'
: ''}"
@click="${() => this.#selectEntry()}"
>
${this.root ? nothing : this.testStateIcon}
<slot name="label" class="mx-2 block flex-initial shrink"></slot>
<slot name="label" class="mx-2 block min-w-0 flex-1"></slot>
</span>
${this.#renderToolbar(hasNoChildren)}
</section>
Expand Down
10 changes: 10 additions & 0 deletions packages/app/src/components/workbench/action-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ export interface CommandRow {

export type ActionTreeRow = GroupRow | CommandRow

/** Identity of a render row. Rows are keyed on it so an expand/collapse moves
* each element with the action it belongs to; reusing whatever element sat at
* that index hands the new action the old row's local state. Namespaced so a
* callId that reads as a number can't collide with a command index. */
export function rowKey(row: ActionTreeRow): string {
return row.kind === 'group'
? `group:${row.group.callId}`
: `command:${row.commandIndex}`
}

/** Command indices anywhere under a group, nested groups included. */
export function collectCommandIndices(group: TraceActionGroupNode): number[] {
const indices: number[] = []
Expand Down
6 changes: 2 additions & 4 deletions packages/app/src/components/workbench/actionItems/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export class CommandItem extends ActionItem {
if (!this.entry) {
return
}
this.requestReveal()
window.dispatchEvent(
// Typed as the contract, which is what makes the two nullable fields below
// a compile error rather than a blank chip in the Log tab.
Expand Down Expand Up @@ -117,10 +118,7 @@ export class CommandItem extends ActionItem {
@click="${() => this.#highlightLine()}"
>
${this.iconChip(this.#renderIcon(entry.command))}
<code
class="text-[12.5px] flex-wrap text-left break-all ${this.failed
? 'text-chartsRed'
: ''}"
<code class="label text-[12.5px] ${this.failed ? 'text-chartsRed' : ''}"
>${capitalizeAssertLabel(entry.title ?? entry.command)}</code
>
${this.renderTime()}
Expand Down
3 changes: 2 additions & 1 deletion packages/app/src/components/workbench/actionItems/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class GroupItem extends ActionItem {
}

#toggle() {
this.requestReveal()
this.dispatchEvent(
new CustomEvent('group-toggle', {
detail: { callId: this.group?.callId, expanded: this.expanded },
Expand All @@ -51,7 +52,7 @@ export class GroupItem extends ActionItem {
: ''}"
></icon-mdi-chevron-right>
<span
class="text-[12.5px] font-medium text-left break-all ${this.failed
class="label text-[12.5px] font-medium ${this.failed
? 'text-chartsRed'
: ''}"
>${this.group.title}</span
Expand Down
44 changes: 43 additions & 1 deletion packages/app/src/components/workbench/actionItems/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { formatDuration, durationHeat, type DurationHeat } from './duration.js'

export type ActionEntry = TraceMutation | CommandLog

/** What a row reports as its identity: an action by reference, a group by key. */
export type RowRevealKey = string | ActionEntry

/** Icon sized to sit inside the `.ic` chip rendered by `iconChip`. */
export const ICON_CLASS = 'w-[15px] h-[15px] block shrink-0'

Expand All @@ -33,6 +36,27 @@ export class ActionItem extends Element {
@property({ type: Boolean, reflect: true })
failed = false

/** Whether this row shows its label in full. Owned by the list, which keeps
* exactly one row revealed; `active` can't drive it because the player clock
* marks the action at the playhead, expanding rows nobody touched. */
@property({ type: Boolean, reflect: true })
revealed = false

/** Identity this row echoes back on click, for the list to compare. */
@property({ attribute: false })
revealKey?: RowRevealKey

/** Click-only by construction, which is what keeps playback out of it. */
protected requestReveal() {
this.dispatchEvent(
new CustomEvent<RowRevealKey | undefined>('row-reveal', {
detail: this.revealKey,
bubbles: true,
composed: true
})
)
}
Comment thread
vishnuv688 marked this conversation as resolved.

static styles = [
...Element.styles,
css`
Expand All @@ -44,6 +68,24 @@ export class ActionItem extends Element {

button {
position: relative;
/* Same height for every row whether or not it carries an icon chip
(chip 26px + its 4px margins). */
min-height: 34px;
}

/* One line per row; clicking a row is what reflows its full text. */
.label {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: left;
padding-right: 8px;
}
:host([revealed]) .label {
white-space: normal;
overflow-wrap: anywhere;
}

.ic {
Expand Down Expand Up @@ -102,7 +144,7 @@ export class ActionItem extends Element {
const heatCls = HEAT_CLASS[durationHeat(this.duration)]
return html`
<span
class="text-[10px] grow-0 shrink rounded-xl ml-auto px-1.5 py-px font-medium ${heatCls}"
class="text-[10px] flex-none whitespace-nowrap rounded-xl ml-auto px-1.5 py-px font-medium ${heatCls}"
>${formatDuration(this.duration)}</span
>
`
Expand Down
7 changes: 4 additions & 3 deletions packages/app/src/components/workbench/actionItems/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class MutationItem extends ActionItem {
${this.iconChip(
html`<icon-mdi-pencil class="${ICON_CLASS}"></icon-mdi-pencil>`
)}
<span class="flex-grow text-left"
<span class="label"
>element attribute "<code>${mutation.attributeName}</code>"
changed</span
>
Expand All @@ -42,7 +42,7 @@ export class MutationItem extends ActionItem {
${this.iconChip(
html`<icon-mdi-document class="${ICON_CLASS}"></icon-mdi-document>`
)}
<span class="flex-grow text-left">Document loaded</span>
<span class="label">Document loaded</span>
${this.renderTime()}
`
}
Expand All @@ -52,7 +52,7 @@ export class MutationItem extends ActionItem {
class="${ICON_CLASS}"
></icon-mdi-family-tree>`
)}
<span class="flex-grow text-left">
<span class="label">
${this.#renderNodeAmount(mutation.addedNodes, 'added')}
${mutation.addedNodes.length && mutation.removedNodes.length
? ' and '
Expand All @@ -78,6 +78,7 @@ export class MutationItem extends ActionItem {
}

#selectMutation() {
this.requestReveal()
const event = new CustomEvent('app-mutation-select', { detail: this.entry })
window.dispatchEvent(event)
this.requestUpdate()
Expand Down
Loading
Loading