mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
fix: keep the assistant's chat usable in a short side panel it shares with another plugin
This commit is contained in:
@@ -52,6 +52,13 @@ describe('PluginIframeSlot', () => {
|
||||
expect(wrapper.style.flex).toBe('1 1 0%');
|
||||
});
|
||||
|
||||
it('keeps a filling frame at its minimum height when it is given one', async () => {
|
||||
activePlugin = plugin(['ui:composer-sidebar']);
|
||||
const { container } = render(<PluginIframeSlot pluginId="p1" slot="composer-sidebar-right" extraProps={{}} fill minFillHeight={280} />);
|
||||
await waitFor(() => expect(createSlotInstance).toHaveBeenCalled());
|
||||
expect((container.querySelector('[data-plugin-iframe-slot]') as HTMLElement).style.minHeight).toBe('280px');
|
||||
});
|
||||
|
||||
it('mounts a banner-only plugin without the opened message', async () => {
|
||||
activePlugin = plugin(['ui:email-banner']);
|
||||
render(<PluginIframeSlot pluginId="p1" slot="email-banner" extraProps={{ email: EMAIL }} />);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import React from 'react';
|
||||
import { PluginSlot, SHARED_FILL_MIN_HEIGHT } from '../plugin-slot';
|
||||
|
||||
let offers: Array<{ pluginId: string; fill?: boolean }> = [];
|
||||
vi.mock('@/hooks/use-plugin-slot-offers', () => ({ usePluginSlotOffers: () => offers }));
|
||||
vi.mock('../plugin-iframe-slot', () => ({
|
||||
PluginIframeSlot: ({ pluginId, minFillHeight }: { pluginId: string; minFillHeight?: number }) => (
|
||||
<div data-testid={pluginId} data-min={minFillHeight ?? ''} />
|
||||
),
|
||||
}));
|
||||
|
||||
describe('PluginSlot', () => {
|
||||
it('keeps a filling frame usable next to another frame in a short panel', () => {
|
||||
offers = [{ pluginId: 'notes' }, { pluginId: 'chat', fill: true }];
|
||||
render(<PluginSlot name="email-detail-sidebar" />);
|
||||
expect(screen.getByTestId('chat').dataset.min).toBe(String(SHARED_FILL_MIN_HEIGHT));
|
||||
expect(screen.getByTestId('notes').dataset.min).toBe('');
|
||||
});
|
||||
|
||||
it('lets a filling frame alone in its panel take whatever height there is', () => {
|
||||
offers = [{ pluginId: 'chat', fill: true }];
|
||||
render(<PluginSlot name="email-detail-sidebar" />);
|
||||
expect(screen.getByTestId('chat').dataset.min).toBe('');
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,8 @@ interface Props {
|
||||
extraProps?: Record<string, unknown>;
|
||||
/** The frame fills its panel and scrolls inside, instead of growing with its content. */
|
||||
fill?: boolean;
|
||||
/** With `fill`: the height the frame keeps however little room its panel has. */
|
||||
minFillHeight?: number;
|
||||
/**
|
||||
* Told whether the frame is up: false when the plugin's shouldShow says no
|
||||
* or its frame fails to start, true once it has started. For a slot that
|
||||
@@ -45,7 +47,7 @@ interface FloatingDropdownState {
|
||||
ariaLabel?: string;
|
||||
}
|
||||
|
||||
export function PluginIframeSlot({ pluginId, slot, extraProps, fill = false, onShownChange }: Props) {
|
||||
export function PluginIframeSlot({ pluginId, slot, extraProps, fill = false, minFillHeight, onShownChange }: Props) {
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
const instanceRef = useRef<SandboxInstance | null>(null);
|
||||
const onShownChangeRef = useRef(onShownChange);
|
||||
@@ -189,7 +191,7 @@ export function PluginIframeSlot({ pluginId, slot, extraProps, fill = false, onS
|
||||
ref={wrapperRef}
|
||||
style={inline
|
||||
? { display: 'inline-block', verticalAlign: 'middle', height, width: width ?? 0, maxWidth: '100%', overflow: 'hidden' }
|
||||
: fill ? { flex: '1 1 0%', minHeight: 0 } : { height, minHeight: height }}
|
||||
: fill ? { flex: '1 1 0%', minHeight: minFillHeight ?? 0 } : { height, minHeight: height }}
|
||||
data-plugin-iframe-slot={`${pluginId}:${slot}`}
|
||||
data-plugin-iframe-fill={fill || undefined}
|
||||
/>
|
||||
|
||||
@@ -13,6 +13,9 @@ interface PluginSlotProps {
|
||||
extraProps?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/** Height a filling frame keeps when it shares its panel with other frames. */
|
||||
export const SHARED_FILL_MIN_HEIGHT = 280;
|
||||
|
||||
export function PluginSlot({ name, className, extraProps }: PluginSlotProps) {
|
||||
// Without a retired plugin's offer while Bulwark does its job (host-features.ts).
|
||||
const offers = usePluginSlotOffers(name);
|
||||
@@ -21,8 +24,11 @@ export function PluginSlot({ name, className, extraProps }: PluginSlotProps) {
|
||||
|
||||
// Inline slots line up next to each other in a toolbar row. A panel slot
|
||||
// with a frame that fills the panel takes the panel's height and shares
|
||||
// it between such frames.
|
||||
// it between such frames. Next to another frame it keeps a usable height
|
||||
// however short the panel is: the panel scrolls instead of squeezing it
|
||||
// away, input and all.
|
||||
const fills = offers.some((offer) => offer.fill);
|
||||
const minFillHeight = offers.length > 1 ? SHARED_FILL_MIN_HEIGHT : undefined;
|
||||
return (
|
||||
<div
|
||||
className={isInlineSlot(name)
|
||||
@@ -37,6 +43,7 @@ export function PluginSlot({ name, className, extraProps }: PluginSlotProps) {
|
||||
slot={name}
|
||||
extraProps={extraProps}
|
||||
fill={offer.fill}
|
||||
minFillHeight={offer.fill ? minFillHeight : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user