mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-01 16:12:02 +00:00
Merge pull request #1139 from dealerweb/fix/filter-rule-wrap
fix: wrap a filter condition's values in the expanded rule view
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import React from 'react';
|
||||
import { VisualRuleSummary } from '../filter-settings';
|
||||
import type { FilterRule } from '@/lib/jmap/sieve-types';
|
||||
|
||||
const rule = (overrides: Partial<FilterRule>): FilterRule => ({
|
||||
id: 'r1',
|
||||
name: 'Rule',
|
||||
enabled: true,
|
||||
matchType: 'all',
|
||||
conditions: [],
|
||||
actions: [],
|
||||
stopProcessing: false,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
/** The value items in the chip of the condition on `field` (next-intl returns keys). */
|
||||
const valuesOf = (field: string) => {
|
||||
const chip = screen.getByText(`condition_fields.${field}`).parentElement!;
|
||||
return { chip, values: [...chip.lastElementChild!.children].map((el) => el.textContent) };
|
||||
};
|
||||
|
||||
// The expanded view wraps a condition's values between each other, so each
|
||||
// value is its own item and each "or" leads the value after it - a wrapped
|
||||
// line then starts with "or", and no value is split from its "or".
|
||||
describe('filter rule summary (expanded view)', () => {
|
||||
it('keeps the values of one condition apart, each "or" leading the value after it', () => {
|
||||
render(<VisualRuleSummary rule={rule({
|
||||
matchType: 'any',
|
||||
conditions: [{
|
||||
field: 'from',
|
||||
comparator: 'contains',
|
||||
value: ['ReportingEMail@autoscout24.com', 'gebrauchtwagen.expert', 'noreply@webmobil24.com'],
|
||||
}],
|
||||
actions: [{ type: 'move', value: 'INBOX/Boersen' }],
|
||||
})} />);
|
||||
expect(valuesOf('from').values).toEqual([
|
||||
'“ReportingEMail@autoscout24.com”',
|
||||
'or “gebrauchtwagen.expert”',
|
||||
'or “noreply@webmobil24.com”',
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps "or" inside the condition, apart from the "and" between conditions', () => {
|
||||
render(<VisualRuleSummary rule={rule({
|
||||
matchType: 'all',
|
||||
conditions: [
|
||||
{ field: 'from', comparator: 'contains', value: ['a@example.com', 'b@example.com'] },
|
||||
{ field: 'body', comparator: 'contains', value: 'Jochen Seith' },
|
||||
],
|
||||
})} />);
|
||||
const from = valuesOf('from');
|
||||
expect(from.chip).toHaveTextContent('or “b@example.com”');
|
||||
expect(from.chip).not.toHaveTextContent('and');
|
||||
expect(valuesOf('body').values).toEqual(['“Jochen Seith”']);
|
||||
expect(from.chip.contains(screen.getByText('and'))).toBe(false);
|
||||
});
|
||||
|
||||
it('shows no value for a condition that has none', () => {
|
||||
render(<VisualRuleSummary rule={rule({
|
||||
conditions: [{ field: 'attachment', comparator: 'has_any', value: '' }],
|
||||
})} />);
|
||||
const chip = screen.getByText('condition_fields.attachment').parentElement!;
|
||||
expect([...chip.children].map((el) => el.textContent)).toEqual([
|
||||
'condition_fields.attachment',
|
||||
'comparators.has_any',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -86,7 +86,7 @@ function RuleSummary({ rule }: { rule: FilterRule }) {
|
||||
);
|
||||
}
|
||||
|
||||
function VisualRuleSummary({ rule }: { rule: FilterRule }) {
|
||||
export function VisualRuleSummary({ rule }: { rule: FilterRule }) {
|
||||
const t = useTranslations("settings.filters");
|
||||
|
||||
const joiner = rule.matchType === "all" ? t("and") : t("or");
|
||||
@@ -106,23 +106,24 @@ function VisualRuleSummary({ rule }: { rule: FilterRule }) {
|
||||
{i > 0 && (
|
||||
<span className="text-[10px] text-muted-foreground/70 italic">{joiner}</span>
|
||||
)}
|
||||
<span className="inline-flex items-baseline gap-1 px-1.5 py-px rounded-sm bg-muted/60 text-foreground">
|
||||
<span className="font-medium text-blue-600 dark:text-blue-400">{field}</span>
|
||||
<span className="text-muted-foreground">{comparator}</span>
|
||||
<span className="inline-flex min-w-0 max-w-full items-baseline gap-1 px-1.5 py-px rounded-sm bg-muted/60 text-foreground">
|
||||
<span className="shrink-0 font-medium text-blue-600 dark:text-blue-400">{field}</span>
|
||||
<span className="shrink-0 text-muted-foreground">{comparator}</span>
|
||||
{!(c.field === "attachment" && c.comparator === "has_any") && (
|
||||
<span className="text-foreground">
|
||||
{Array.isArray(c.value)
|
||||
? c.value.map((v, k) => (
|
||||
<span key={k}>
|
||||
{k > 0 && (
|
||||
<span className="text-muted-foreground/70 italic mx-0.5">
|
||||
{t("or")}
|
||||
</span>
|
||||
)}
|
||||
“{v}”
|
||||
</span>
|
||||
))
|
||||
: <>“{c.value}”</>}
|
||||
// The values of one condition stay in its chip, where "or"
|
||||
// cannot be mistaken for the joiner between conditions. They
|
||||
// wrap between each other under the first value, the "or"
|
||||
// leading the next line; a single value too long for any
|
||||
// line breaks inside itself instead of leaving the card.
|
||||
<span className="flex min-w-0 flex-wrap items-baseline gap-x-1 text-foreground">
|
||||
{(Array.isArray(c.value) ? c.value : [c.value]).map((v, k) => (
|
||||
<span key={k} className="min-w-0 wrap-anywhere">
|
||||
{k > 0 && (
|
||||
<span className="text-muted-foreground/70 italic">{t("or")} </span>
|
||||
)}
|
||||
“{v}”
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
@@ -143,9 +144,9 @@ function VisualRuleSummary({ rule }: { rule: FilterRule }) {
|
||||
{i > 0 && (
|
||||
<span className="text-muted-foreground/50">›</span>
|
||||
)}
|
||||
<span className="inline-flex items-baseline gap-1 px-1.5 py-px rounded-sm bg-muted/60 text-foreground">
|
||||
<span className="font-medium text-emerald-600 dark:text-emerald-400">{action}</span>
|
||||
{a.value && <span className="text-muted-foreground">“{a.value}”</span>}
|
||||
<span className="inline-flex min-w-0 max-w-full items-baseline gap-1 px-1.5 py-px rounded-sm bg-muted/60 text-foreground">
|
||||
<span className="shrink-0 font-medium text-emerald-600 dark:text-emerald-400">{action}</span>
|
||||
{a.value && <span className="min-w-0 wrap-anywhere text-muted-foreground">“{a.value}”</span>}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user