Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
Original file line number Diff line number Diff line change
@@ -1,58 +1,118 @@
import type { StoryFn } from '@storybook/vue3-vite';
import { ref } from 'vue';

import N8nPopover from './Popover.vue';
import N8nButton from '../N8nButton/Button.vue';
import N8nInput from '../N8nInput/Input.vue';

export default {
title: 'Atoms/Popover',
component: N8nPopover,
argTypes: {
effect: {
control: 'select',
options: ['dark', 'light'],
enableScrolling: {
control: 'boolean',
},
placement: {
scrollType: {
control: 'select',
options: [
'top',
'top-start',
'top-end',
'bottom',
'bottom-start',
'bottom-end',
'left',
'left-start',
'left-end',
'right',
'right-start',
'right-end',
],
options: ['auto', 'always', 'scroll', 'hover'],
},
disabled: {
control: { type: 'boolean' },
maxHeight: {
control: 'text',
},
},
parameters: {
backgrounds: { default: '--color--background--light-2' },
},
};

const Template: StoryFn = (args, { argTypes }) => ({
setup: () => ({ args }),
props: Object.keys(argTypes),
const Template: StoryFn = (args) => ({
setup() {
const username = ref('');
const email = ref('');
const isOpen = ref(false);

return { args, username, email, isOpen };
},
components: {
N8nPopover,
N8nButton,
N8nInput,
},
template: `<n8n-popover v-bind="args">
<div style="margin:50px; display: inline-block;">
<span>yo</span>
</div>
<template #content>
Popover
</template>
</n8n-popover>`,
template: `
<div style="padding: 50px;">
<N8nPopover v-model:open="isOpen" v-bind="args">
<template #trigger>
<N8nButton type="primary">Open Form</N8nButton>
</template>
<template #content="{ close }">
<div style="display: flex; flex-direction: column; gap: 12px;">
<h3 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 600;">User Information</h3>
<N8nInput
v-model="username"
placeholder="Enter username"
label="Username"
/>
<N8nInput
v-model="email"
placeholder="Enter email"
label="Email"
type="email"
/>
<div style="display: flex; gap: 8px; margin-top: 8px;">
<N8nButton size="small" type="primary">Save</N8nButton>
<N8nButton size="small" type="secondary" @click="close">Cancel</N8nButton>
</div>
</div>
</template>
</N8nPopover>
</div>
`,
});

export const Popover = Template.bind({});
Popover.args = {
content: '...',
export const SimpleExample = Template.bind({});
SimpleExample.args = {};
SimpleExample.storyName = 'With Form Inputs';

const ScrollableTemplate: StoryFn = (args) => ({
setup() {
const isOpen = ref(false);
return { args, isOpen };
},
components: {
N8nPopover,
N8nButton,
},
template: `
<div style="padding: 50px;">
<N8nPopover v-model:open="isOpen" v-bind="args">
<template #trigger>
<N8nButton type="primary">Open Scrollable Menu</N8nButton>
</template>
<template #content="{ close }">
<div style="display: flex; flex-direction: column; gap: 8px;">
<h3 style="margin: 0 0 12px 0; font-size: 14px; font-weight: 600;">Menu Items</h3>
<div v-for="i in 20" :key="i"
style="padding: 8px 12px; background: var(--color--background); border-radius: 4px; cursor: pointer; min-height: 40px; display: flex; align-items: center;"
@click="close"
>
Menu Item {{ i }}: Some description text that explains what this item does
</div>
</div>
</template>
</N8nPopover>
</div>
`,
});

export const WithScrolling = ScrollableTemplate.bind({});
WithScrolling.args = {
maxHeight: '300px',
enableScrolling: true,
scrollType: 'hover',
};
WithScrolling.storyName = 'With Scrollable Content';

export const AlwaysVisibleScrollbars = ScrollableTemplate.bind({});
AlwaysVisibleScrollbars.args = {
maxHeight: '250px',
enableScrolling: true,
scrollType: 'always',
};
AlwaysVisibleScrollbars.storyName = 'Always Visible Scrollbars';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { render } from '@testing-library/vue';
import { mount } from '@vue/test-utils';
import { vi } from 'vitest';

import N8nPopoverReka from './N8nPopoverReka.vue';
import N8nPopover from './Popover.vue';

const defaultStubs = {
PopoverContent: {
Expand All @@ -23,9 +23,9 @@ const defaultStubs = {
},
};

describe('N8nPopoverReka', () => {
describe('N8nPopover', () => {
it('should render correctly with default props', () => {
const wrapper = render(N8nPopoverReka, {
const wrapper = render(N8nPopover, {
props: {},
global: {
stubs: defaultStubs,
Expand All @@ -44,7 +44,7 @@ describe('N8nPopoverReka', () => {
it('should emit update:open with false when close function is called', () => {
let closeFunction: (() => void) | undefined;

const wrapper = render(N8nPopoverReka, {
const wrapper = render(N8nPopover, {
props: {},
global: {
stubs: {
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('N8nPopoverReka', () => {
});

it('should apply maxHeight style when maxHeight prop is provided', () => {
const wrapper = mount(N8nPopoverReka, {
const wrapper = mount(N8nPopover, {
props: {
maxHeight: '200px',
},
Expand All @@ -100,7 +100,7 @@ describe('N8nPopoverReka', () => {
});

it('should not apply maxHeight style when maxHeight prop is not provided', () => {
const wrapper = mount(N8nPopoverReka, {
const wrapper = mount(N8nPopover, {
props: {},
global: {
stubs: defaultStubs,
Expand All @@ -116,7 +116,7 @@ describe('N8nPopoverReka', () => {

describe('auto-focus behavior', () => {
it('should focus an element in the content slot by default', async () => {
const wrapper = render(N8nPopoverReka, {
const wrapper = render(N8nPopover, {
props: { open: true },
slots: {
trigger: '<button />',
Expand All @@ -129,7 +129,7 @@ describe('N8nPopoverReka', () => {
});

it('should suppress auto-focus when suppressAutoFocus is true', async () => {
const wrapper = render(N8nPopoverReka, {
const wrapper = render(N8nPopover, {
props: { open: true, suppressAutoFocus: true },
slots: {
trigger: '<button />',
Expand Down
Loading
Loading