mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-22 12:19:09 +00:00
refactor(editor): Migrate mapper popover to ruka UI (#19564)
This commit is contained in:
@@ -113,4 +113,32 @@ describe('N8nPopoverReka', () => {
|
||||
|
||||
expect(wrapper.props('maxHeight')).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('auto-focus behavior', () => {
|
||||
it('should focus an element in the content slot by default', async () => {
|
||||
const wrapper = render(N8nPopoverReka, {
|
||||
props: { open: true },
|
||||
slots: {
|
||||
trigger: '<button />',
|
||||
content: '<input />',
|
||||
},
|
||||
});
|
||||
const popover = await wrapper.findByRole('dialog');
|
||||
|
||||
expect(popover.contains(document.activeElement)).toBe(true);
|
||||
});
|
||||
|
||||
it('should suppress auto-focus when suppressAutoFocus is true', async () => {
|
||||
const wrapper = render(N8nPopoverReka, {
|
||||
props: { open: true, suppressAutoFocus: true },
|
||||
slots: {
|
||||
trigger: '<button />',
|
||||
content: '<input />',
|
||||
},
|
||||
});
|
||||
const popover = await wrapper.findByRole('dialog');
|
||||
|
||||
expect(popover.contains(document.activeElement)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui';
|
||||
import {
|
||||
PopoverContent,
|
||||
type PopoverContentProps,
|
||||
PopoverPortal,
|
||||
PopoverRoot,
|
||||
type PopoverRootProps,
|
||||
PopoverTrigger,
|
||||
} from 'reka-ui';
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import N8nScrollArea from '../N8nScrollArea/N8nScrollArea.vue';
|
||||
|
||||
interface Props {
|
||||
open?: boolean;
|
||||
interface Props
|
||||
extends Pick<PopoverContentProps, 'side' | 'align' | 'sideFlip' | 'sideOffset' | 'reference'>,
|
||||
Pick<PopoverRootProps, 'open'> {
|
||||
/**
|
||||
* Whether to enable scrolling in the popover content
|
||||
*/
|
||||
enableScrolling?: boolean;
|
||||
/**
|
||||
* Whether to enable slide-in animation
|
||||
*/
|
||||
enableSlideIn?: boolean;
|
||||
/**
|
||||
* Whether to suppress auto-focus behavior when the content includes focusable element
|
||||
*/
|
||||
suppressAutoFocus?: boolean;
|
||||
/**
|
||||
* Scrollbar visibility behavior
|
||||
*/
|
||||
@@ -17,14 +34,18 @@ interface Props {
|
||||
* Popover width
|
||||
*/
|
||||
width?: string;
|
||||
/**
|
||||
* z-index of popover content
|
||||
*/
|
||||
zIndex?: number | CSSProperties['zIndex'];
|
||||
/**
|
||||
* Popover max height
|
||||
*/
|
||||
maxHeight?: string;
|
||||
/**
|
||||
* The preferred alignment against the trigger. May change when collisions occur.
|
||||
* Additional class name set to PopperContent
|
||||
*/
|
||||
align?: 'start' | 'center' | 'end';
|
||||
contentClass?: string;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
@@ -32,15 +53,24 @@ interface Emits {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
open: undefined,
|
||||
maxHeight: undefined,
|
||||
width: undefined,
|
||||
enableScrolling: true,
|
||||
enableSlideIn: true,
|
||||
scrollType: 'hover',
|
||||
align: undefined,
|
||||
sideOffset: 5,
|
||||
sideFlip: undefined,
|
||||
suppressAutoFocus: false,
|
||||
zIndex: 999,
|
||||
});
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
function handleOpenAutoFocus(e: Event) {
|
||||
if (props.suppressAutoFocus) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -49,21 +79,29 @@ const emit = defineEmits<Emits>();
|
||||
<slot name="trigger"></slot>
|
||||
</PopoverTrigger>
|
||||
<PopoverPortal>
|
||||
<PopoverContent side="bottom" :align="align" :side-offset="5" :class="$style.popoverContent">
|
||||
<PopoverContent
|
||||
role="dialog"
|
||||
:side="side"
|
||||
:side-flip="sideFlip"
|
||||
:align="align"
|
||||
:side-offset="sideOffset"
|
||||
:class="[$style.popoverContent, contentClass, { [$style.enableSlideIn]: enableSlideIn }]"
|
||||
:style="{ width, zIndex }"
|
||||
:reference="reference"
|
||||
@open-auto-focus="handleOpenAutoFocus"
|
||||
>
|
||||
<N8nScrollArea
|
||||
v-if="enableScrolling"
|
||||
:max-height="props.maxHeight"
|
||||
:max-height="maxHeight"
|
||||
:type="scrollType"
|
||||
:enable-vertical-scroll="true"
|
||||
:enable-horizontal-scroll="false"
|
||||
>
|
||||
<div :style="{ width }">
|
||||
<slot name="content" :close="() => emit('update:open', false)" />
|
||||
</div>
|
||||
</N8nScrollArea>
|
||||
<div v-else :style="{ width }">
|
||||
<slot name="content" :close="() => emit('update:open', false)" />
|
||||
</div>
|
||||
</N8nScrollArea>
|
||||
<template v-else>
|
||||
<slot name="content" :close="() => emit('update:open', false)" />
|
||||
</template>
|
||||
</PopoverContent>
|
||||
</PopoverPortal>
|
||||
</PopoverRoot>
|
||||
@@ -77,10 +115,12 @@ const emit = defineEmits<Emits>();
|
||||
box-shadow:
|
||||
rgba(0, 0, 0, 0.1) 0 10px 15px -3px,
|
||||
rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
|
||||
animation-duration: 400ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
will-change: transform, opacity;
|
||||
z-index: 999;
|
||||
|
||||
&.enableSlideIn {
|
||||
animation-duration: 400ms;
|
||||
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.popoverContent[data-state='open'][data-side='top'] {
|
||||
|
||||
@@ -4,13 +4,11 @@ exports[`N8nPopoverReka > should render correctly with default props 1`] = `
|
||||
"<mock-popover-root>
|
||||
<mock-popover-trigger><button></button></mock-popover-trigger>
|
||||
<mock-popover-portal>
|
||||
<mock-popover-content>
|
||||
<mock-popover-content role="dialog">
|
||||
<div dir="ltr" style="position: relative; --reka-scroll-area-corner-width: 0px; --reka-scroll-area-corner-height: 0px;" class="scrollAreaRoot">
|
||||
<div data-reka-scroll-area-viewport="" style="overflow-x: hidden; overflow-y: hidden;" class="viewport" tabindex="0">
|
||||
<div>
|
||||
<div>
|
||||
<content></content>
|
||||
</div>
|
||||
<content></content>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
|
||||
Reference in New Issue
Block a user