Skip to content
Draft
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
51 changes: 51 additions & 0 deletions dev/vscode-breadcrumbs/basic-example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VSCode Elements</title>
<link
rel="stylesheet"
href="/node_modules/@vscode/codicons/dist/codicon.css"
id="vscode-codicon-stylesheet"
>
<script
type="module"
src="/node_modules/@vscode-elements/webview-playground/dist/index.js"
></script>
<script type="module" src="/dist/main.js"></script>
<script>
const logEvents = (selector, eventType) => {
document.querySelector(selector).addEventListener(eventType, (ev) => {
console.log(ev);
});
};
</script>
</head>

<body>
<h1>Basic example</h1>
<main>
<vscode-demo>
<vscode-breadcrumbs id="bc-1">
<vscode-breadcrumb-item>
<vscode-icon slot="icon" name="folder"></vscode-icon>
workspace
</vscode-breadcrumb-item>
<vscode-breadcrumb-item>
<vscode-icon slot="icon" name="folder"></vscode-icon>
src
</vscode-breadcrumb-item>
<vscode-breadcrumb-item>
<vscode-icon slot="icon" name="folder"></vscode-icon>
components
</vscode-breadcrumb-item>
<vscode-breadcrumb-item>
<vscode-icon slot="icon" name="file-code"></vscode-icon>
vscode-breadcrumbs.ts
</vscode-breadcrumb-item>
</vscode-breadcrumbs>
</vscode-demo>
</main>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {html, svg} from 'lit';
import {svg} from 'lit';

export const chevronDownIcon = html`
export const chevronDownIcon = svg`
<span class="icon">
<svg
width="16"
Expand All @@ -18,6 +18,20 @@ export const chevronDownIcon = html`
</span>
`;

export const chevronRightIcon = svg`<svg
width="16"
height="16"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.072 8.024L5.715 3.667l.618-.62L11 7.716v.618L6.333 13l-.618-.619 4.357-4.357z"
/>
</svg>`;

export const checkIcon = svg`<svg
width="16"
height="16"
Expand Down
2 changes: 1 addition & 1 deletion src/includes/vscode-select/vscode-select-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {VscElement} from '../VscElement.js';
import {filterOptionsByPattern, highlightRanges} from './helpers.js';
import type {InternalOption, Option, FilterMethod} from './types.js';
import {OptionListController} from './OptionListController.js';
import {checkIcon} from './template-elements.js';
import {checkIcon} from '../icons.js';
import '../../vscode-scrollable/vscode-scrollable.js';

export const VISIBLE_OPTS = 10;
Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ export {VscodeToolbarButton} from './vscode-toolbar-button/index.js';
export {VscodeToolbarContainer} from './vscode-toolbar-container/index.js';
export {VscodeTree} from './vscode-tree/index.js';
export {VscodeTreeItem} from './vscode-tree-item/index.js';
export {VscodeBreadcrumbs} from './vscode-breadcrumbs/index.js';
export {VscodeBreadcrumbItem} from './vscode-breadcrumb-item/index.js';
1 change: 1 addition & 0 deletions src/vscode-breadcrumb-item/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {VscodeBreadcrumbItem} from './vscode-breadcrumb-item.js';
71 changes: 71 additions & 0 deletions src/vscode-breadcrumb-item/vscode-breadcrumb-item.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {css, CSSResultGroup} from 'lit';
import defaultStyles from '../includes/default.styles.js';

const styles: CSSResultGroup = [
defaultStyles,
css`
:host {
display: inline-block;
}

:host(:focus) {
outline: none;
}

:host(:focus) .root {
color: var(--vscode-breadcrumb-focusForeground, #e0e0e0);
outline: 1px solid var(--vscode-focusBorder, #0078d4);
text-decoration: underline;
}

:host(.selected) {
color: var(
--vscode-breadcrumb-activeSelectionForeground,
var(--vscode-breadcrumb-focusForeground, inherit)
);
}

.root {
display: flex;
align-items: center;
color: var(--vscode-breadcrumb-foreground, inherit);
outline: none;
cursor: pointer;
}

:host(:hover) .root {
color: var(--vscode-breadcrumb-focusForeground, #e0e0e0);
}

.icon {
height: 16px;
width: 16px;
}

.icon.has-icon {
margin-right: 6px;
}

.separator {
user-select: none;
color: var(--vscode-breadcrumb-foreground, inherit);
height: 16px;
opacity: 0.7;
width: 16px;
}

:host(:first-child) .separator {
display: none;
}

:host(:first-child) .root {
margin-left: 16px;
}

:host(:last-child) .root {
margin-right: 8px;
}
`,
];

export default styles;
60 changes: 60 additions & 0 deletions src/vscode-breadcrumb-item/vscode-breadcrumb-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {TemplateResult, html} from 'lit';
import {customElement, VscElement} from '../includes/VscElement.js';
import styles from './vscode-breadcrumb-item.styles.js';
import {chevronRightIcon} from '../includes/icons.js';
import {state} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js';

/**
* @tag vscode-breadcrumb-item
*
* Slot the label/icon as content.
*
* @cssprop [--vscode-breadcrumb-foreground=inherit] - Breadcrumb text and icon color
* @cssprop [--vscode-breadcrumb-focusForeground=var(--vscode-breadcrumb-foreground, inherit)] - Text color when an item has focus
* @cssprop [--vscode-breadcrumb-activeSelectionForeground=var(--vscode-breadcrumb-focusForeground, inherit)] - Text color for the selected item
*/
@customElement('vscode-breadcrumb-item')
export class VscodeBreadcrumbItem extends VscElement {
static override styles = styles;

override connectedCallback(): void {
super.connectedCallback();
if (!this.hasAttribute('tabindex')) {
this.tabIndex = -1;
}
this.setAttribute('role', 'listitem');
}

@state()
_hasIcon = false;

private _handleSlotChange(ev: Event) {
const slot = ev.target as HTMLSlotElement;

this._hasIcon = slot.assignedElements().length > 0;
}

override render(): TemplateResult {
return html`
<div class="root">
<div class="separator" aria-hidden="true" part="separator">
<slot name="icon-separator">${chevronRightIcon}</slot>
</div>
<div
class=${classMap({icon: true, 'has-icon': this._hasIcon})}
part="icon"
>
<slot name="icon" @slotchange=${this._handleSlotChange}></slot>
</div>
<div class="content" part="content"><slot></slot></div>
</div>
`;
}
}

declare global {
interface HTMLElementTagNameMap {
'vscode-breadcrumb-item': VscodeBreadcrumbItem;
}
}
1 change: 1 addition & 0 deletions src/vscode-breadcrumbs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {VscodeBreadcrumbs} from './vscode-breadcrumbs.js';
35 changes: 35 additions & 0 deletions src/vscode-breadcrumbs/vscode-breadcrumbs.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {css, CSSResultGroup} from 'lit';
import defaultStyles from '../includes/default.styles.js';

const styles: CSSResultGroup = [
defaultStyles,
css`
:host {
display: block;
width: 100%;
outline: none;
}

.container {
-ms-overflow-style: none; /* IE 10+ */
align-items: center;
background: var(--vscode-breadcrumb-background, transparent);
display: flex;
height: 22px;
overflow-x: auto;
overflow-y: hidden;
position: relative;
scrollbar-width: none; /* Firefox */
}

.container::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}

::slotted(vscode-breadcrumb-item) {
flex: 0 0 auto;
}
`,
];

export default styles;
97 changes: 97 additions & 0 deletions src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {expect, fixture, html} from '@open-wc/testing';
import './vscode-breadcrumbs.js';
import '../vscode-breadcrumb-item/vscode-breadcrumb-item.js';
import {VscodeBreadcrumbs} from './vscode-breadcrumbs.js';

describe('vscode-breadcrumbs', () => {
it('is defined', () => {
const el = document.createElement('vscode-breadcrumbs');
expect(el).to.be.instanceOf(VscodeBreadcrumbs);
});

it('focuses and selects items on click', async () => {
const el = (await fixture(html`
<vscode-breadcrumbs>
<vscode-breadcrumb-item>Root</vscode-breadcrumb-item>
<vscode-breadcrumb-item>src</vscode-breadcrumb-item>
<vscode-breadcrumb-item>index.ts</vscode-breadcrumb-item>
</vscode-breadcrumbs>
`)) as VscodeBreadcrumbs;

const items = el.querySelectorAll('vscode-breadcrumb-item');

const selectPromise = new Promise<CustomEvent>((resolve) => {
const handler = (e: Event) => {
el.removeEventListener('vsc-select', handler);
resolve(e as CustomEvent);
};
el.addEventListener('vsc-select', handler, {once: true});
});
(items[1] as HTMLElement).click();
const ev = await selectPromise;

expect((items[1] as HTMLElement).classList.contains('selected')).to.be.true;

Check failure on line 33 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (macos-latest)

Expected an assignment or function call and instead saw an expression

Check failure on line 33 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (windows-latest)

Expected an assignment or function call and instead saw an expression

Check failure on line 33 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (ubuntu-latest)

Expected an assignment or function call and instead saw an expression
expect(ev.detail.index).to.equal(1);
});

it('supports keyboard navigation and selection', async () => {
const el = (await fixture(html`
<vscode-breadcrumbs>
<vscode-breadcrumb-item>Root</vscode-breadcrumb-item>
<vscode-breadcrumb-item>src</vscode-breadcrumb-item>
<vscode-breadcrumb-item>index.ts</vscode-breadcrumb-item>
</vscode-breadcrumbs>
`)) as VscodeBreadcrumbs;

// Focus the last item by default
const items = el.querySelectorAll('vscode-breadcrumb-item');
await el.updateComplete;

// Move left, then select
el.dispatchEvent(
new KeyboardEvent('keydown', {key: 'ArrowLeft', bubbles: true})
);
el.dispatchEvent(
new KeyboardEvent('keydown', {key: 'Enter', bubbles: true})
);

await el.updateComplete;

expect((items[1] as HTMLElement).classList.contains('selected')).to.be.true;

Check failure on line 60 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (macos-latest)

Expected an assignment or function call and instead saw an expression

Check failure on line 60 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (windows-latest)

Expected an assignment or function call and instead saw an expression

Check failure on line 60 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (ubuntu-latest)

Expected an assignment or function call and instead saw an expression
});

it('sets aria-current on the last item and aria-label on the host by default', async () => {
const el = (await fixture(html`
<vscode-breadcrumbs>
<vscode-breadcrumb-item>Root</vscode-breadcrumb-item>
<vscode-breadcrumb-item>src</vscode-breadcrumb-item>
<vscode-breadcrumb-item>index.ts</vscode-breadcrumb-item>
</vscode-breadcrumbs>
`)) as VscodeBreadcrumbs;

const items = el.querySelectorAll('vscode-breadcrumb-item');
await el.updateComplete;

// Host should have an aria-label
expect(el.getAttribute('aria-label')).to.equal('Breadcrumb');

// Last item should have aria-current="page"
expect((items[2] as HTMLElement).getAttribute('aria-current')).to.equal(
'page'
);
// Other items should not have aria-current
expect((items[0] as HTMLElement).hasAttribute('aria-current')).to.be.false;

Check failure on line 83 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (macos-latest)

Expected an assignment or function call and instead saw an expression

Check failure on line 83 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (windows-latest)

Expected an assignment or function call and instead saw an expression

Check failure on line 83 in src/vscode-breadcrumbs/vscode-breadcrumbs.test.ts

View workflow job for this annotation

GitHub Actions / Verify (ubuntu-latest)

Expected an assignment or function call and instead saw an expression
});

it('does not override an existing aria-label on the host', async () => {
const el = (await fixture(html`
<vscode-breadcrumbs aria-label="Custom label">
<vscode-breadcrumb-item>Root</vscode-breadcrumb-item>
<vscode-breadcrumb-item>src</vscode-breadcrumb-item>
</vscode-breadcrumbs>
`)) as VscodeBreadcrumbs;

await el.updateComplete;
expect(el.getAttribute('aria-label')).to.equal('Custom label');
});
});
Loading
Loading