Skip to content

Commit

Permalink
feat(vue-menu): add disabled state for items
Browse files Browse the repository at this point in the history
  • Loading branch information
devCrossNet committed Sep 25, 2021
1 parent 57df108 commit 5ddc0fa
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 20 deletions.
Expand Up @@ -14,6 +14,7 @@ $menu-item-bg-active: var(--brand-interaction-primary-hovered);
$menu-item-color-active: var(--brand-text-inverse-high);
$menu-item-icon-size: $space-20;
$menu-item-icon-size-gap: $space-8;
$menu-item-disabled-opacity: 0.6;

// separator
$menu-separator-border: 1px solid var(--brand-border-default-low);
48 changes: 33 additions & 15 deletions src/components/data-display/VueMenu/VueMenu.spec.ts
Expand Up @@ -11,7 +11,7 @@ describe('VueMenu.vue', () => {
{ label: 'Value 1', value: 'Value 1', description: 'Description 1', leadingIcon: 'hashtag' },
{ label: 'Value 2', value: 'Value 2', description: 'Description 2', trailingIcon: 'hashtag' },
{ label: '', value: 'separator' },
{ label: 'Value 3', value: 'Value 3', description: 'Description 3', leadingIcon: 'hashtag' },
{ label: 'Value 3', value: 'Value 3', description: 'Description 3', leadingIcon: 'hashtag', disabled: true },
{ label: 'Value 4', value: 'Value 4', description: 'Description 4', trailingIcon: 'hashtag' },
],
},
Expand All @@ -35,14 +35,15 @@ describe('VueMenu.vue', () => {
test('should select 1st item and emit click event via keyboard', async () => {
const { getByTestId, emitted } = harness;
const firstItem = getByTestId('Value 1-0');
const menu = firstItem.parentElement;

await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowLeft', code: 'ArrowLeft' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'Enter', code: 'Enter' });
await fireEvent.keyDown(menu, { key: 'ArrowLeft', code: 'ArrowLeft' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'Enter', code: 'Enter' });

expect(emitted().click).toEqual([
[
Expand All @@ -59,14 +60,15 @@ describe('VueMenu.vue', () => {
test('should select last item and emit click event via keyboard', async () => {
const { getByTestId, emitted } = harness;
const firstItem = getByTestId('Value 1-0');
const menu = firstItem.parentElement;

await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowRight', code: 'ArrowRight' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(firstItem.parentElement, { key: 'Enter', code: 'Enter' });
await fireEvent.keyDown(menu, { key: 'ArrowRight', code: 'ArrowRight' });
await fireEvent.keyDown(menu, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(menu, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(menu, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(menu, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(menu, { key: 'ArrowUp', code: 'ArrowUp' });
await fireEvent.keyDown(menu, { key: 'Enter', code: 'Enter' });

expect(emitted().click).toEqual([
[
Expand All @@ -79,4 +81,20 @@ describe('VueMenu.vue', () => {
],
]);
});

test('disabled item should not emit click event', async () => {
const { getByTestId, emitted } = harness;
const firstItem = getByTestId('Value 1-0');
const menu = firstItem.parentElement;

await fireEvent.keyDown(menu, { key: 'ArrowRight', code: 'ArrowRight' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'ArrowDown', code: 'ArrowDown' });
await fireEvent.keyDown(menu, { key: 'Enter', code: 'Enter' });

await fireEvent.click(getByTestId('Value 3-3'));

expect(emitted().click).toBeFalsy();
});
});
2 changes: 1 addition & 1 deletion src/components/data-display/VueMenu/VueMenu.stories.ts
Expand Up @@ -14,7 +14,7 @@ story.add(
return {
items: [
{ label: 'Value 1', value: 'Value 1', description: 'Description 1', leadingIcon: 'cog' },
{ label: 'Value 2', value: 'Value 2', description: 'Description 2', trailingIcon: 'times' },
{ label: 'Value 2', value: 'Value 2', description: 'Description 2', trailingIcon: 'times', disabled: true },
{ label: '', value: 'separator' },
{ label: 'Value 3', value: 'Value 3', description: 'Description 3', leadingIcon: 'cog' },
{ label: 'Value 4', value: 'Value 4', description: 'Description 4', trailingIcon: 'times' },
Expand Down
21 changes: 17 additions & 4 deletions src/components/data-display/VueMenu/VueMenu.vue
Expand Up @@ -4,13 +4,17 @@
v-for="(item, idx) in items"
:key="`${item.value}-${idx}`"
:data-testid="`${item.value}-${idx}`"
:class="[selectedItemIndex === idx ? $style.active : '', item.value === 'separator' ? $style.separator : '']"
:class="[
selectedItemIndex === idx ? $style.active : '',
item.value === 'separator' ? $style.separator : '',
item.disabled && $style.disabled,
]"
:tabindex="item.value === 'separator' ? -1 : 0"
@mouseenter="selectedItemIndex = idx"
@mouseleave="selectedItemIndex = -1"
@focus="selectedItemIndex = idx"
@blur="selectedItemIndex = -1"
@click.stop.prevent="onItemClick(item)"
@click.stop.prevent="item.disabled ? null : onItemClick(item)"
>
<slot v-if="item.value !== 'separator'" name="option" :option="item">
<div v-if="item.leadingIcon" :class="$style.leading">
Expand All @@ -19,7 +23,7 @@

<div :class="$style.value">
<vue-text :color="selectedItemIndex === idx ? 'text-inverse-high' : 'text-high'">{{ item.label }}</vue-text>
<vue-text v-if="item.description" look="support" color="text-inverse-medium">
<vue-text v-if="item.description" look="support" color="text-inverse-low">
{{ item.description }}
</vue-text>
</div>
Expand Down Expand Up @@ -78,7 +82,11 @@ export default defineComponent({
const onKeyDown = (e: KeyboardEvent) => {
checkForPropagation(e);
if (['Enter', 'Space'].includes(e.code) && selectedItemIndex.value > -1) {
if (
['Enter', 'Space'].includes(e.code) &&
selectedItemIndex.value > -1 &&
!items.value[selectedItemIndex.value].disabled
) {
onItemClick(items.value[selectedItemIndex.value]);
} else if (e.code === 'ArrowDown') {
handleSelection(getNewIndex('down'));
Expand Down Expand Up @@ -168,6 +176,11 @@ export default defineComponent({
height: 0;
border-top: $menu-separator-border;
}
&.disabled {
opacity: $menu-item-disabled-opacity;
cursor: not-allowed;
}
}
}
</style>
1 change: 1 addition & 0 deletions src/interfaces/IItem.ts
Expand Up @@ -4,4 +4,5 @@ export interface IItem {
description?: string;
leadingIcon?: string;
trailingIcon?: string;
disabled?: boolean;
}

0 comments on commit 5ddc0fa

Please sign in to comment.