Skip to content
Open
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
16 changes: 15 additions & 1 deletion nx2/blocks/shared/menu/menu.css
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

.menu-item {
display: flex;
align-items: center;
align-items: flex-start;
gap: var(--s2-spacing-100);
width: 100%;
padding: var(--s2-spacing-75) var(--s2-spacing-100);
Expand All @@ -41,6 +41,15 @@
background: var(--s2-gray-200);
}

.menu-item:disabled {
color: light-dark(var(--s2-gray-500), var(--s2-gray-600));
cursor: default;
}

.menu-item:disabled:hover {
background: none;
}

.menu-item-icon {
display: flex;
align-items: center;
Expand All @@ -63,6 +72,11 @@
}
}

.menu-item:disabled .menu-item-icon path,
.menu-item:disabled .menu-item-icon text {
fill: light-dark(var(--s2-gray-500), var(--s2-gray-600));
}

.menu-item-text {
display: flex;
flex-direction: column;
Expand Down
9 changes: 6 additions & 3 deletions nx2/blocks/shared/menu/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class NxMenu extends LitElement {
if (e.newState !== 'open') return;
this._trigger?.toggleAttribute('data-active', true);
this._trigger?.setAttribute('aria-expanded', 'true');
const first = this.items?.find((i) => !i.divider && !i.section);
const first = this.items?.find((i) => !i.divider && !i.section && !i.disabled);
this._active = first?.id;
this.updateComplete.then(() => {
if (!this.ignoreFocus) this.shadowRoot.querySelector(`[data-id="${this._active}"]`)?.focus();
Expand Down Expand Up @@ -85,6 +85,7 @@ class NxMenu extends LitElement {
}

_select(item) {
if (!item || item.disabled) return;
this.close();
this.dispatchEvent(new CustomEvent('select', { detail: { id: item.id }, bubbles: true, composed: true }));
}
Expand Down Expand Up @@ -119,9 +120,11 @@ class NxMenu extends LitElement {
data-id=${item.id}
class="menu-item ${item.id === this._active ? 'menu-item-active' : ''}"
type="button"
?disabled=${item.disabled}
aria-disabled=${item.disabled ? 'true' : nothing}
@click=${() => this._select(item)}
@mouseenter=${() => { this._active = item.id; }}
@focus=${() => { this._active = item.id; }}
@mouseenter=${() => { if (!item.disabled) this._active = item.id; }}
@focus=${() => { if (!item.disabled) this._active = item.id; }}
>
${item.icon ? html`<svg class="menu-item-icon" viewBox="0 0 20 20" aria-hidden="true"><use href="${codeBase}/img/icons/s2-icon-${item.icon}-20-n.svg#icon"></use></svg>` : nothing}
<span class="menu-item-text">
Expand Down
2 changes: 1 addition & 1 deletion nx2/blocks/shared/utils/list-nav.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export function listKeydown(key, {
items, active, itemKey, shadowRoot, setActive, onSelect, onClose,
focusActiveItem = true,
}) {
const selectable = items?.filter((i) => !i.divider && !i.section) ?? [];
const selectable = items?.filter((i) => !i.divider && !i.section && !i.disabled) ?? [];
if (!selectable.length) return false;

const curIdx = selectable.findIndex((i) => i[itemKey] === active);
Expand Down
41 changes: 41 additions & 0 deletions nx2/test/unit/nx/blocks/shared/menu/menu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,44 @@ describe('nx-menu description', () => {
expect(label.textContent).to.equal('Report a bug');
});
});

describe('nx-menu disabled', () => {
afterEach(() => {
document.querySelectorAll('nx-menu').forEach((el) => el.remove());
});

it('renders a disabled item as a disabled button', async () => {
const el = await createMenu([{ id: 'lib', label: 'Open block library', disabled: true }]);
const btn = el.shadowRoot.querySelector('[data-id="lib"]');
expect(btn.disabled).to.be.true;
expect(btn.getAttribute('aria-disabled')).to.equal('true');
});

it('does not emit select when a disabled item is clicked', async () => {
const el = await createMenu([{ id: 'lib', label: 'Open block library', disabled: true }]);
let selected = false;
el.addEventListener('select', () => { selected = true; });
el.shadowRoot.querySelector('[data-id="lib"]').click();
expect(selected).to.be.false;
});

it('skips disabled items when picking the initial active item', async () => {
const el = await createMenu([
{ id: 'lib', label: 'Open block library', disabled: true },
{ id: 'insert', label: 'Insert block' },
]);
el._onMenuToggle({ newState: 'open' });
await el.updateComplete;
expect(el._active).to.equal('insert');
});

it('skips disabled items during keyboard navigation', async () => {
const el = await createMenu([
{ id: 'insert', label: 'Insert block' },
{ id: 'lib', label: 'Open block library', disabled: true },
]);
el._active = 'insert';
el.handleKey('ArrowDown');
expect(el._active).to.equal('insert');
});
});
Loading