@qoretechnologies/reqraft 0.10.13-pr.74.gccefa54 → 0.10.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/CLAUDE.md +8 -11
- package/dist/components/form/engine/FormEngine.d.ts +1 -1
- package/dist/helpers/common.d.ts +0 -5
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +1 -20
- package/dist/helpers/common.js.map +1 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -7
- package/dist/index.js.map +1 -1
- package/dist/providers/StorageProvider.d.ts +13 -0
- package/dist/providers/StorageProvider.d.ts.map +1 -1
- package/dist/providers/StorageProvider.js +29 -5
- package/dist/providers/StorageProvider.js.map +1 -1
- package/package.json +3 -3
- package/src/helpers/common.ts +0 -19
- package/src/hooks/useStorage/useStorage.stories.tsx +52 -0
- package/src/index.tsx +0 -5
- package/src/providers/StorageProvider.tsx +30 -4
- package/dist/components/composer/Composer.d.ts +0 -108
- package/dist/components/composer/Composer.d.ts.map +0 -1
- package/dist/components/composer/Composer.js +0 -238
- package/dist/components/composer/Composer.js.map +0 -1
- package/dist/components/composer/helpers.d.ts +0 -21
- package/dist/components/composer/helpers.d.ts.map +0 -1
- package/dist/components/composer/helpers.js +0 -75
- package/dist/components/composer/helpers.js.map +0 -1
- package/dist/components/composer/index.d.ts +0 -3
- package/dist/components/composer/index.d.ts.map +0 -1
- package/dist/components/composer/index.js +0 -19
- package/dist/components/composer/index.js.map +0 -1
- package/dist/components/imageLightbox/ImageLightbox.d.ts +0 -17
- package/dist/components/imageLightbox/ImageLightbox.d.ts.map +0 -1
- package/dist/components/imageLightbox/ImageLightbox.js +0 -129
- package/dist/components/imageLightbox/ImageLightbox.js.map +0 -1
- package/dist/components/imageLightbox/index.d.ts +0 -2
- package/dist/components/imageLightbox/index.d.ts.map +0 -1
- package/dist/components/imageLightbox/index.js +0 -18
- package/dist/components/imageLightbox/index.js.map +0 -1
- package/dist/components/supportTicket/AttachmentChips.d.ts +0 -10
- package/dist/components/supportTicket/AttachmentChips.d.ts.map +0 -1
- package/dist/components/supportTicket/AttachmentChips.js +0 -119
- package/dist/components/supportTicket/AttachmentChips.js.map +0 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +0 -35
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +0 -1
- package/dist/components/supportTicket/InterfaceReferenceTags.js +0 -31
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +0 -1
- package/dist/components/supportTicket/ReferencePicker.d.ts +0 -41
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +0 -1
- package/dist/components/supportTicket/ReferencePicker.js +0 -86
- package/dist/components/supportTicket/ReferencePicker.js.map +0 -1
- package/dist/components/supportTicket/TicketHeader.d.ts +0 -86
- package/dist/components/supportTicket/TicketHeader.d.ts.map +0 -1
- package/dist/components/supportTicket/TicketHeader.js +0 -149
- package/dist/components/supportTicket/TicketHeader.js.map +0 -1
- package/dist/components/supportTicket/TicketMetaTags.d.ts +0 -32
- package/dist/components/supportTicket/TicketMetaTags.d.ts.map +0 -1
- package/dist/components/supportTicket/TicketMetaTags.js +0 -21
- package/dist/components/supportTicket/TicketMetaTags.js.map +0 -1
- package/dist/components/supportTicket/TicketReplyBox.d.ts +0 -63
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +0 -1
- package/dist/components/supportTicket/TicketReplyBox.js +0 -115
- package/dist/components/supportTicket/TicketReplyBox.js.map +0 -1
- package/dist/components/supportTicket/fileToAttachment.d.ts +0 -9
- package/dist/components/supportTicket/fileToAttachment.d.ts.map +0 -1
- package/dist/components/supportTicket/fileToAttachment.js +0 -28
- package/dist/components/supportTicket/fileToAttachment.js.map +0 -1
- package/dist/components/supportTicket/index.d.ts +0 -9
- package/dist/components/supportTicket/index.d.ts.map +0 -1
- package/dist/components/supportTicket/index.js +0 -25
- package/dist/components/supportTicket/index.js.map +0 -1
- package/dist/components/supportTicket/meta.d.ts +0 -63
- package/dist/components/supportTicket/meta.d.ts.map +0 -1
- package/dist/components/supportTicket/meta.js +0 -79
- package/dist/components/supportTicket/meta.js.map +0 -1
- package/dist/components/ticketReferences/TicketReferences.d.ts +0 -52
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +0 -1
- package/dist/components/ticketReferences/TicketReferences.js +0 -453
- package/dist/components/ticketReferences/TicketReferences.js.map +0 -1
- package/dist/components/ticketReferences/index.d.ts +0 -3
- package/dist/components/ticketReferences/index.d.ts.map +0 -1
- package/dist/components/ticketReferences/index.js +0 -6
- package/dist/components/ticketReferences/index.js.map +0 -1
- package/dist/components/ticketThread/TicketThread.d.ts +0 -52
- package/dist/components/ticketThread/TicketThread.d.ts.map +0 -1
- package/dist/components/ticketThread/TicketThread.js +0 -248
- package/dist/components/ticketThread/TicketThread.js.map +0 -1
- package/dist/components/ticketThread/index.d.ts +0 -3
- package/dist/components/ticketThread/index.d.ts.map +0 -1
- package/dist/components/ticketThread/index.js +0 -7
- package/dist/components/ticketThread/index.js.map +0 -1
- package/dist/helpers/scrollbar.d.ts +0 -2
- package/dist/helpers/scrollbar.d.ts.map +0 -1
- package/dist/helpers/scrollbar.js +0 -25
- package/dist/helpers/scrollbar.js.map +0 -1
- package/src/components/composer/Composer.stories.tsx +0 -238
- package/src/components/composer/Composer.tsx +0 -545
- package/src/components/composer/helpers.ts +0 -74
- package/src/components/composer/index.ts +0 -2
- package/src/components/imageLightbox/ImageLightbox.stories.tsx +0 -156
- package/src/components/imageLightbox/ImageLightbox.tsx +0 -169
- package/src/components/imageLightbox/index.ts +0 -1
- package/src/components/supportTicket/AttachmentChips.stories.tsx +0 -122
- package/src/components/supportTicket/AttachmentChips.tsx +0 -142
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +0 -134
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +0 -85
- package/src/components/supportTicket/ReferencePicker.stories.tsx +0 -296
- package/src/components/supportTicket/ReferencePicker.tsx +0 -237
- package/src/components/supportTicket/TicketConversation.stories.tsx +0 -122
- package/src/components/supportTicket/TicketHeader.stories.tsx +0 -183
- package/src/components/supportTicket/TicketHeader.tsx +0 -247
- package/src/components/supportTicket/TicketMetaTags.stories.tsx +0 -103
- package/src/components/supportTicket/TicketMetaTags.tsx +0 -85
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +0 -355
- package/src/components/supportTicket/TicketReplyBox.tsx +0 -146
- package/src/components/supportTicket/fileToAttachment.ts +0 -24
- package/src/components/supportTicket/index.ts +0 -8
- package/src/components/supportTicket/meta.ts +0 -145
- package/src/components/ticketReferences/TicketReferences.stories.tsx +0 -301
- package/src/components/ticketReferences/TicketReferences.tsx +0 -899
- package/src/components/ticketReferences/index.ts +0 -2
- package/src/components/ticketThread/TicketThread.stories.tsx +0 -751
- package/src/components/ticketThread/TicketThread.tsx +0 -650
- package/src/components/ticketThread/index.ts +0 -6
- package/src/helpers/scrollbar.ts +0 -32
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
3
|
-
import { StoryMeta } from '../../types';
|
|
4
|
-
import { InterfaceReferenceTags } from './InterfaceReferenceTags';
|
|
5
|
-
import { IInterfaceReference } from './meta';
|
|
6
|
-
|
|
7
|
-
// Several references, including two of the same kind (workflow), as `{kind, name}`
|
|
8
|
-
// snapshots — no `resolveInterfaceIcon` is passed, so the built-in per-kind default
|
|
9
|
-
// icons are exercised.
|
|
10
|
-
const REFERENCES: IInterfaceReference[] = [
|
|
11
|
-
{ interface_kind: 'workflow', interface_name: 'order-sync:1.2', reference_id: 'r1' },
|
|
12
|
-
{ interface_kind: 'workflow', interface_name: 'invoice-sync:2.3', reference_id: 'r2' },
|
|
13
|
-
{ interface_kind: 'service', interface_name: 'notification-svc', reference_id: 'r3' },
|
|
14
|
-
{ interface_kind: 'job', interface_name: 'nightly-cleanup', reference_id: 'r4' },
|
|
15
|
-
];
|
|
16
|
-
|
|
17
|
-
const meta = {
|
|
18
|
-
component: InterfaceReferenceTags,
|
|
19
|
-
title: 'Components/InterfaceReferenceTags',
|
|
20
|
-
args: {
|
|
21
|
-
references: REFERENCES,
|
|
22
|
-
label: 'References',
|
|
23
|
-
},
|
|
24
|
-
} as StoryMeta<typeof InterfaceReferenceTags>;
|
|
25
|
-
|
|
26
|
-
export default meta;
|
|
27
|
-
type Story = StoryObj<typeof meta>;
|
|
28
|
-
|
|
29
|
-
/** The ticket-header use: a "References" label followed by one chip per reference. */
|
|
30
|
-
export const Default: Story = {
|
|
31
|
-
parameters: {
|
|
32
|
-
docs: {
|
|
33
|
-
description: {
|
|
34
|
-
story:
|
|
35
|
-
"Renders the ticket-header use — a 'References' label followed by one chip per reference.",
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
async play({ canvasElement }) {
|
|
40
|
-
const canvas = within(canvasElement);
|
|
41
|
-
await expect(canvas.getByText('References')).toBeInTheDocument();
|
|
42
|
-
await expect(canvas.getByText('order-sync:1.2')).toBeInTheDocument();
|
|
43
|
-
await expect(canvas.getByText('invoice-sync:2.3')).toBeInTheDocument();
|
|
44
|
-
await expect(canvas.getByText('notification-svc')).toBeInTheDocument();
|
|
45
|
-
await expect(canvas.getByText('nightly-cleanup')).toBeInTheDocument();
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* When `onInterfaceClick` is passed the chips become interactive; clicking one
|
|
51
|
-
* calls it with that reference (the customer view, which can open the interface on
|
|
52
|
-
* its own instance, wires this).
|
|
53
|
-
*/
|
|
54
|
-
export const Clickable: Story = {
|
|
55
|
-
parameters: {
|
|
56
|
-
docs: {
|
|
57
|
-
description: {
|
|
58
|
-
story:
|
|
59
|
-
'Renders interactive chips by passing onInterfaceClick — clicking a chip calls it with that reference, as the customer view (which can open the interface on its own instance) wires.',
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
args: { onInterfaceClick: fn() },
|
|
64
|
-
async play({ canvasElement, args }) {
|
|
65
|
-
const canvas = within(canvasElement);
|
|
66
|
-
await userEvent.click(canvas.getByText('invoice-sync:2.3'));
|
|
67
|
-
await waitFor(() =>
|
|
68
|
-
expect(args.onInterfaceClick).toHaveBeenCalledWith(
|
|
69
|
-
expect.objectContaining({
|
|
70
|
-
interface_kind: 'workflow',
|
|
71
|
-
interface_name: 'invoice-sync:2.3',
|
|
72
|
-
})
|
|
73
|
-
)
|
|
74
|
-
);
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/** The in-thread use: no label — the chips sit under a message. */
|
|
79
|
-
export const NoLabel: Story = {
|
|
80
|
-
parameters: {
|
|
81
|
-
docs: {
|
|
82
|
-
description: {
|
|
83
|
-
story:
|
|
84
|
-
'Renders the in-thread use with label undefined — no label, just the chips as they sit under a message.',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
args: { label: undefined },
|
|
89
|
-
async play({ canvasElement }) {
|
|
90
|
-
const canvas = within(canvasElement);
|
|
91
|
-
await expect(canvas.queryByText('References')).not.toBeInTheDocument();
|
|
92
|
-
await expect(canvas.getByText('order-sync:1.2')).toBeInTheDocument();
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* With no references the component renders nothing — no empty header, no chips —
|
|
98
|
-
* so a ticket without references shows a clean gap rather than a stray "References:"
|
|
99
|
-
* label. The dashed frame below is a story-only caption; the empty space inside it
|
|
100
|
-
* is the component.
|
|
101
|
-
*/
|
|
102
|
-
export const Empty: Story = {
|
|
103
|
-
parameters: {
|
|
104
|
-
docs: {
|
|
105
|
-
description: {
|
|
106
|
-
story:
|
|
107
|
-
'Renders the empty case — with no references the component renders nothing (no header, no chips), shown inside a dashed story-only caption frame.',
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
args: { references: [] },
|
|
112
|
-
decorators: [
|
|
113
|
-
(Story) => (
|
|
114
|
-
<div
|
|
115
|
-
style={{
|
|
116
|
-
border: '1px dashed rgba(150, 150, 150, 0.4)',
|
|
117
|
-
borderRadius: 6,
|
|
118
|
-
padding: 12,
|
|
119
|
-
fontSize: 12,
|
|
120
|
-
opacity: 0.6,
|
|
121
|
-
}}
|
|
122
|
-
>
|
|
123
|
-
No references → the row renders nothing (no empty header, no chips):
|
|
124
|
-
<Story />
|
|
125
|
-
</div>
|
|
126
|
-
),
|
|
127
|
-
],
|
|
128
|
-
async play({ canvasElement }) {
|
|
129
|
-
// The dashed frame + caption are the decorator; the component itself renders
|
|
130
|
-
// nothing — no header text, no chips.
|
|
131
|
-
await expect(within(canvasElement).queryByText('References')).not.toBeInTheDocument();
|
|
132
|
-
await expect(within(canvasElement).queryByText('order-sync:1.2')).not.toBeInTheDocument();
|
|
133
|
-
},
|
|
134
|
-
};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { ReqoreControlGroup, ReqoreP, ReqoreTag } from '@qoretechnologies/reqore';
|
|
2
|
-
import { TSizes } from '@qoretechnologies/reqore/dist/constants/sizes';
|
|
3
|
-
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
4
|
-
import { ComponentProps } from 'react';
|
|
5
|
-
import { defaultInterfaceIcon, IInterfaceReference } from './meta';
|
|
6
|
-
|
|
7
|
-
export interface IInterfaceReferenceTagsProps {
|
|
8
|
-
references?: IInterfaceReference[];
|
|
9
|
-
/** Optional leading label, e.g. "References" in a ticket header. Omitted (the
|
|
10
|
-
* default) inside the thread, where the chips sit under a message. */
|
|
11
|
-
label?: string;
|
|
12
|
-
/** Resolve a per-kind icon. Consumers with their own icon vocabulary (the IDE)
|
|
13
|
-
* pass one; when omitted the built-in per-kind default is used. */
|
|
14
|
-
resolveInterfaceIcon?: (kind: string) => IReqoreIconName;
|
|
15
|
-
/** Open a referenced interface; chips are static (informational) when omitted —
|
|
16
|
-
* e.g. the staff view, which can't reach the customer's instance. */
|
|
17
|
-
onInterfaceClick?: (reference: IInterfaceReference) => void;
|
|
18
|
-
size?: TSizes;
|
|
19
|
-
/** Chip styling — pass `intent`/`customTheme` (e.g. `{ main: 'custom1' }`) to tint
|
|
20
|
-
* the reference chips to a surface's accent (the Qonsole/helpdesk composer does). */
|
|
21
|
-
intent?: ComponentProps<typeof ReqoreTag>['intent'];
|
|
22
|
-
customTheme?: ComponentProps<typeof ReqoreTag>['customTheme'];
|
|
23
|
-
/** Resolve a per-reference image — e.g. a qog's trigger-app logo, the way the
|
|
24
|
-
* qogs list does. When this (or the reference's own `logo`) returns a value, the
|
|
25
|
-
* chip shows the image in place of the kind icon. */
|
|
26
|
-
resolveInterfaceImage?: (reference: IInterfaceReference) => string | undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The chips for the Qorus interfaces a ticket or message references, as
|
|
31
|
-
* `{kind, name}` snapshots. The single renderer for interface references across
|
|
32
|
-
* every support surface — the ticket header (with a "References" label), the
|
|
33
|
-
* thread (per message, no label), and the Qonsole card — so a reference looks
|
|
34
|
-
* identical everywhere. Renders nothing when there are no references.
|
|
35
|
-
*/
|
|
36
|
-
export const InterfaceReferenceTags = ({
|
|
37
|
-
references,
|
|
38
|
-
label,
|
|
39
|
-
resolveInterfaceIcon,
|
|
40
|
-
onInterfaceClick,
|
|
41
|
-
size = 'small',
|
|
42
|
-
intent,
|
|
43
|
-
customTheme,
|
|
44
|
-
resolveInterfaceImage,
|
|
45
|
-
}: IInterfaceReferenceTagsProps) => {
|
|
46
|
-
if (!references?.length) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
const resolveIcon = resolveInterfaceIcon ?? defaultInterfaceIcon;
|
|
50
|
-
return (
|
|
51
|
-
<ReqoreControlGroup verticalAlign='center' wrap gapSize='small'>
|
|
52
|
-
{label ? (
|
|
53
|
-
<ReqoreP size='small' effect={{ opacity: 0.6 }}>
|
|
54
|
-
{label}
|
|
55
|
-
</ReqoreP>
|
|
56
|
-
) : null}
|
|
57
|
-
{references.map((reference) => {
|
|
58
|
-
// an app-triggered qog shows the trigger app's logo (stored, or resolved by
|
|
59
|
-
// a viewer with the app catalogue); everything else shows its kind glyph.
|
|
60
|
-
const image = reference.logo ?? resolveInterfaceImage?.(reference);
|
|
61
|
-
return (
|
|
62
|
-
<ReqoreTag
|
|
63
|
-
key={
|
|
64
|
-
reference.reference_id ??
|
|
65
|
-
`${reference.interface_kind}:${reference.interface_name}`
|
|
66
|
-
}
|
|
67
|
-
icon={image ? undefined : resolveIcon(reference.interface_kind)}
|
|
68
|
-
leftIconProps={image ? { image } : undefined}
|
|
69
|
-
labelKey={reference.interface_kind}
|
|
70
|
-
label={reference.interface_name}
|
|
71
|
-
size={size}
|
|
72
|
-
intent={intent}
|
|
73
|
-
customTheme={customTheme}
|
|
74
|
-
tooltip={
|
|
75
|
-
onInterfaceClick
|
|
76
|
-
? `Open ${reference.interface_kind} ${reference.interface_name}`
|
|
77
|
-
: `${reference.interface_kind}: ${reference.interface_name}`
|
|
78
|
-
}
|
|
79
|
-
onClick={onInterfaceClick ? () => onInterfaceClick(reference) : undefined}
|
|
80
|
-
/>
|
|
81
|
-
);
|
|
82
|
-
})}
|
|
83
|
-
</ReqoreControlGroup>
|
|
84
|
-
);
|
|
85
|
-
};
|
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
import { ReqoreButton, ReqoreUIProvider } from '@qoretechnologies/reqore';
|
|
2
|
-
import { StoryObj } from '@storybook/react-vite';
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
5
|
-
import { StoryMeta } from '../../types';
|
|
6
|
-
import { InterfaceReferenceTags } from './InterfaceReferenceTags';
|
|
7
|
-
import { IInterfaceReference } from './meta';
|
|
8
|
-
import { ReferencePicker } from './ReferencePicker';
|
|
9
|
-
import { TicketReplyBox } from './TicketReplyBox';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* The two-pane reference browser (master-detail: kinds on the left, that kind's
|
|
13
|
-
* interfaces on the right) with a single search bar that scopes both panes. Rendered
|
|
14
|
-
* **on top** of the composer and toggled by its "Reference interfaces" action.
|
|
15
|
-
*/
|
|
16
|
-
const meta = {
|
|
17
|
-
component: ReferencePicker,
|
|
18
|
-
title: 'Components/ReferencePicker',
|
|
19
|
-
} as StoryMeta<typeof ReferencePicker>;
|
|
20
|
-
export default meta;
|
|
21
|
-
type Story = StoryObj<typeof meta>;
|
|
22
|
-
|
|
23
|
-
const ACCENT = { main: 'custom1' } as const;
|
|
24
|
-
|
|
25
|
-
const KINDS = [
|
|
26
|
-
'workflow',
|
|
27
|
-
'service',
|
|
28
|
-
'job',
|
|
29
|
-
'fsm',
|
|
30
|
-
'connection',
|
|
31
|
-
'mapper',
|
|
32
|
-
'class',
|
|
33
|
-
'value-map',
|
|
34
|
-
'qog',
|
|
35
|
-
'ai-collection',
|
|
36
|
-
'ai-endpoint',
|
|
37
|
-
'ai-guardrail',
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
/* Stands in for the host's fetch: in the IDE these come off the customer's live
|
|
41
|
-
* instance one kind at a time. */
|
|
42
|
-
const CATALOGUE: Record<string, string[]> = {
|
|
43
|
-
workflow: ['order-sync:1.2', 'invoice-export:2.0', 'partner-recon:1.1'],
|
|
44
|
-
service: ['notifier', 'audit-logger', 'partner-gateway'],
|
|
45
|
-
job: ['nightly-recon', 'cleanup-temp', 'sla-report'],
|
|
46
|
-
fsm: ['onboarding', 'escalation'],
|
|
47
|
-
connection: ['sftp-partner', 'pgsql-omq', 'salesforce-prod'],
|
|
48
|
-
mapper: ['csv-to-order', 'order-to-invoice'],
|
|
49
|
-
class: ['OrderUtils', 'PartnerApi'],
|
|
50
|
-
'value-map': ['country-codes', 'currency'],
|
|
51
|
-
qog: ['telegram-intake', 'stripe-billing'],
|
|
52
|
-
'ai-collection': ['kb-support'],
|
|
53
|
-
'ai-endpoint': ['gpt-router'],
|
|
54
|
-
'ai-guardrail': ['pii-filter'],
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const darkTheme = (Story: () => JSX.Element) => (
|
|
58
|
-
<ReqoreUIProvider theme={{ main: '#121212', intents: { custom1: '#762f7e' } }}>
|
|
59
|
-
<div style={{ padding: 20, background: '#121212' }}>
|
|
60
|
-
<Story />
|
|
61
|
-
</div>
|
|
62
|
-
</ReqoreUIProvider>
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
/** Drives the picker the way a host does: owns the selected kind, resolves that
|
|
66
|
-
* kind's interfaces, and accumulates what gets picked. */
|
|
67
|
-
const Hosted = ({ withComposer }: { withComposer?: boolean }) => {
|
|
68
|
-
const [kind, setKind] = useState<string>('workflow');
|
|
69
|
-
const [picked, setPicked] = useState<IInterfaceReference[]>([]);
|
|
70
|
-
const [open, setOpen] = useState<boolean>(true);
|
|
71
|
-
|
|
72
|
-
const picker = (
|
|
73
|
-
<ReferencePicker
|
|
74
|
-
kinds={KINDS}
|
|
75
|
-
kind={kind}
|
|
76
|
-
onKindChange={setKind}
|
|
77
|
-
items={CATALOGUE[kind] ?? []}
|
|
78
|
-
picked={picked}
|
|
79
|
-
onAdd={(reference) => setPicked((current) => [...current, reference])}
|
|
80
|
-
/>
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
if (!withComposer) {
|
|
84
|
-
return picker;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<div style={{ maxWidth: 780 }}>
|
|
89
|
-
{open ? <div style={{ marginBottom: 10 }}>{picker}</div> : null}
|
|
90
|
-
<TicketReplyBox
|
|
91
|
-
onSend={fn()}
|
|
92
|
-
placeholder='Write a reply…'
|
|
93
|
-
aboveInput={
|
|
94
|
-
picked.length ? (
|
|
95
|
-
<InterfaceReferenceTags size='tiny' customTheme={ACCENT} references={picked} />
|
|
96
|
-
) : undefined
|
|
97
|
-
}
|
|
98
|
-
footerActions={
|
|
99
|
-
<ReqoreButton
|
|
100
|
-
className='references-toggle'
|
|
101
|
-
icon='LinksLine'
|
|
102
|
-
minimal
|
|
103
|
-
flat
|
|
104
|
-
badge={picked.length || undefined}
|
|
105
|
-
active={open}
|
|
106
|
-
customTheme={ACCENT}
|
|
107
|
-
tooltip={open ? 'Hide references' : 'Reference interfaces'}
|
|
108
|
-
onClick={() => setOpen((shown) => !shown)}
|
|
109
|
-
/>
|
|
110
|
-
}
|
|
111
|
-
/>
|
|
112
|
-
</div>
|
|
113
|
-
);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
/*
|
|
117
|
-
* Queried by role, not by text: ReqoreButton renders its label in a pair of spans (an
|
|
118
|
-
* active one and an aria-hidden inactive one) to cross-fade, so getByText matches twice
|
|
119
|
-
* for every button. The accessible name collapses that pair back to one.
|
|
120
|
-
*/
|
|
121
|
-
const option = (canvas: ReturnType<typeof within>, name: string) =>
|
|
122
|
-
canvas.getByRole('button', { name });
|
|
123
|
-
const missingOption = (canvas: ReturnType<typeof within>, name: string) =>
|
|
124
|
-
canvas.queryByRole('button', { name });
|
|
125
|
-
|
|
126
|
-
/** Picking an interface adds it as a reference — and it stops being offered, so the
|
|
127
|
-
* same thing can't be referenced twice. */
|
|
128
|
-
export const Default: Story = {
|
|
129
|
-
parameters: {
|
|
130
|
-
docs: {
|
|
131
|
-
description: {
|
|
132
|
-
story:
|
|
133
|
-
"Renders the two-pane reference browser and, on picking an interface, adds it as a reference and drops it from the pane so the same thing can't be referenced twice.",
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
decorators: [darkTheme],
|
|
138
|
-
render: () => <Hosted />,
|
|
139
|
-
async play({ canvasElement }) {
|
|
140
|
-
const canvas = within(canvasElement);
|
|
141
|
-
await userEvent.click(await canvas.findByRole('button', { name: 'order-sync:1.2' }));
|
|
142
|
-
// it moves out of the pane once picked
|
|
143
|
-
await waitFor(() => expect(missingOption(canvas, 'order-sync:1.2')).not.toBeInTheDocument());
|
|
144
|
-
// its siblings are untouched
|
|
145
|
-
await expect(option(canvas, 'invoice-export:2.0')).toBeInTheDocument();
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/** Selecting a kind on the left swaps the right pane to that kind's interfaces. */
|
|
150
|
-
export const SwitchesKind: Story = {
|
|
151
|
-
parameters: {
|
|
152
|
-
docs: {
|
|
153
|
-
description: {
|
|
154
|
-
story:
|
|
155
|
-
"Renders the picker and shows that selecting a kind on the left swaps the right pane to that kind's interfaces.",
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
decorators: [darkTheme],
|
|
160
|
-
render: () => <Hosted />,
|
|
161
|
-
async play({ canvasElement }) {
|
|
162
|
-
const canvas = within(canvasElement);
|
|
163
|
-
await expect(option(canvas, 'order-sync:1.2')).toBeInTheDocument();
|
|
164
|
-
await userEvent.click(option(canvas, 'connection'));
|
|
165
|
-
await waitFor(() => expect(option(canvas, 'sftp-partner')).toBeInTheDocument());
|
|
166
|
-
await expect(missingOption(canvas, 'order-sync:1.2')).not.toBeInTheDocument();
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
/** One search scopes both panes: the kind list and the selected kind's interfaces. */
|
|
171
|
-
export const SearchScopesBothPanes: Story = {
|
|
172
|
-
parameters: {
|
|
173
|
-
docs: {
|
|
174
|
-
description: {
|
|
175
|
-
story:
|
|
176
|
-
"Renders the picker and shows its single search box scoping both panes at once — filtering the selected kind's interfaces and the kind list together.",
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
decorators: [darkTheme],
|
|
181
|
-
render: () => <Hosted />,
|
|
182
|
-
async play({ canvasElement }) {
|
|
183
|
-
const canvas = within(canvasElement);
|
|
184
|
-
const search = canvasElement.querySelector(
|
|
185
|
-
'input[placeholder*="Search kinds"]'
|
|
186
|
-
) as HTMLInputElement;
|
|
187
|
-
|
|
188
|
-
await userEvent.type(search, 'sync');
|
|
189
|
-
// right pane: only the matching interface survives
|
|
190
|
-
await waitFor(() =>
|
|
191
|
-
expect(missingOption(canvas, 'invoice-export:2.0')).not.toBeInTheDocument()
|
|
192
|
-
);
|
|
193
|
-
await expect(option(canvas, 'order-sync:1.2')).toBeInTheDocument();
|
|
194
|
-
// left pane: no kind is called "sync", so the kind list empties out
|
|
195
|
-
await expect(canvas.getByText('No kinds')).toBeInTheDocument();
|
|
196
|
-
|
|
197
|
-
// a kind name matches: that kind stays, and every other kind drops away
|
|
198
|
-
await userEvent.clear(search);
|
|
199
|
-
await userEvent.type(search, 'connect');
|
|
200
|
-
await waitFor(() => expect(option(canvas, 'connection')).toBeInTheDocument());
|
|
201
|
-
await expect(missingOption(canvas, 'workflow')).not.toBeInTheDocument();
|
|
202
|
-
},
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
/** The right pane says it's still fetching rather than claiming the kind is empty. */
|
|
206
|
-
export const Loading: Story = {
|
|
207
|
-
parameters: {
|
|
208
|
-
docs: {
|
|
209
|
-
description: {
|
|
210
|
-
story:
|
|
211
|
-
"Renders the loading state, where the right pane reports it is still fetching ('Loading workflows…') rather than claiming the kind is empty.",
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
|
-
decorators: [darkTheme],
|
|
216
|
-
render: () => (
|
|
217
|
-
<ReferencePicker
|
|
218
|
-
kinds={KINDS}
|
|
219
|
-
kind='workflow'
|
|
220
|
-
onKindChange={fn()}
|
|
221
|
-
items={[]}
|
|
222
|
-
loading
|
|
223
|
-
onAdd={fn()}
|
|
224
|
-
/>
|
|
225
|
-
),
|
|
226
|
-
async play({ canvasElement }) {
|
|
227
|
-
const canvas = within(canvasElement);
|
|
228
|
-
await expect(canvas.getByText('Loading workflows…')).toBeInTheDocument();
|
|
229
|
-
await expect(canvas.queryByText('No matching workflow')).not.toBeInTheDocument();
|
|
230
|
-
},
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* The host's fetch failed. The pane says so and offers a retry — an empty list here
|
|
235
|
-
* would read as "this instance has no connections", which is a different and much more
|
|
236
|
-
* alarming claim than "we couldn't ask". The specific failure is the tooltip.
|
|
237
|
-
*/
|
|
238
|
-
export const LoadFailed: Story = {
|
|
239
|
-
parameters: {
|
|
240
|
-
docs: {
|
|
241
|
-
description: {
|
|
242
|
-
story:
|
|
243
|
-
"Renders the fetch-failure state, where the pane shows the error and a retry action instead of an empty list that would read as 'this instance has no connections'.",
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
decorators: [darkTheme],
|
|
248
|
-
render: () => (
|
|
249
|
-
<ReferencePicker
|
|
250
|
-
kinds={KINDS}
|
|
251
|
-
kind='connection'
|
|
252
|
-
onKindChange={fn()}
|
|
253
|
-
items={[]}
|
|
254
|
-
error='Could not reach the Qorus instance'
|
|
255
|
-
onRetry={fn()}
|
|
256
|
-
onAdd={fn()}
|
|
257
|
-
/>
|
|
258
|
-
),
|
|
259
|
-
async play({ canvasElement }) {
|
|
260
|
-
const canvas = within(canvasElement);
|
|
261
|
-
const retry = await canvas.findByRole('button', { name: /Couldn't load connections/ });
|
|
262
|
-
await expect(retry).toBeInTheDocument();
|
|
263
|
-
// the failure replaces the list rather than sitting alongside an empty one
|
|
264
|
-
await expect(canvas.queryByText('No matching connection')).not.toBeInTheDocument();
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
/** In place: above the composer, toggled by the composer's own action, with the picked
|
|
269
|
-
* references shown as chips over the input. */
|
|
270
|
-
export const AboveTheComposer: Story = {
|
|
271
|
-
parameters: {
|
|
272
|
-
docs: {
|
|
273
|
-
description: {
|
|
274
|
-
story:
|
|
275
|
-
"Renders the picker in place above the composer, toggled by the composer's own action, with picked references surfacing as chips over the input that persist when the picker is closed.",
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
decorators: [darkTheme],
|
|
280
|
-
render: () => <Hosted withComposer />,
|
|
281
|
-
async play({ canvasElement }) {
|
|
282
|
-
const canvas = within(canvasElement);
|
|
283
|
-
await userEvent.click(await canvas.findByRole('button', { name: 'order-sync:1.2' }));
|
|
284
|
-
// the pick leaves the pane and surfaces as a chip on the composer
|
|
285
|
-
await waitFor(() => expect(missingOption(canvas, 'order-sync:1.2')).not.toBeInTheDocument());
|
|
286
|
-
await expect(canvas.getAllByText('order-sync:1.2').length).toBeGreaterThan(0);
|
|
287
|
-
|
|
288
|
-
const toggle = canvasElement.querySelector('.references-toggle') as HTMLElement;
|
|
289
|
-
await userEvent.click(toggle);
|
|
290
|
-
// the picker closes; the chip stays, because the reference is still attached
|
|
291
|
-
await waitFor(() =>
|
|
292
|
-
expect(missingOption(canvas, 'invoice-export:2.0')).not.toBeInTheDocument()
|
|
293
|
-
);
|
|
294
|
-
await expect(canvas.getAllByText('order-sync:1.2').length).toBeGreaterThan(0);
|
|
295
|
-
},
|
|
296
|
-
};
|