@qoretechnologies/reqraft 0.10.25 → 0.10.26
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 +52 -23
- package/design/COMPACT_ENGINE_REDESIGN.md +156 -0
- package/design/FORM_ENGINE_COMPACT_UX_PLAN.md +353 -0
- package/dist/components/FocusedEditing/index.d.ts +9 -1
- package/dist/components/FocusedEditing/index.d.ts.map +1 -1
- package/dist/components/FocusedEditing/index.js +2 -2
- package/dist/components/FocusedEditing/index.js.map +1 -1
- package/dist/components/composer/Composer.d.ts +108 -0
- package/dist/components/composer/Composer.d.ts.map +1 -0
- package/dist/components/composer/Composer.js +238 -0
- package/dist/components/composer/Composer.js.map +1 -0
- package/dist/components/composer/helpers.d.ts +21 -0
- package/dist/components/composer/helpers.d.ts.map +1 -0
- package/dist/components/composer/helpers.js +75 -0
- package/dist/components/composer/helpers.js.map +1 -0
- package/dist/components/composer/index.d.ts +3 -0
- package/dist/components/composer/index.d.ts.map +1 -0
- package/dist/components/composer/index.js +19 -0
- package/dist/components/composer/index.js.map +1 -0
- package/dist/components/form/engine/CompactRow.d.ts.map +1 -1
- package/dist/components/form/engine/CompactRow.js +307 -110
- package/dist/components/form/engine/CompactRow.js.map +1 -1
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +123 -106
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts +107 -11
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +618 -179
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/compactRowContext.d.ts +9 -0
- package/dist/components/form/engine/compactRowContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowContext.js.map +1 -1
- package/dist/components/form/engine/compactRowStyles.d.ts +7 -3
- package/dist/components/form/engine/compactRowStyles.d.ts.map +1 -1
- package/dist/components/form/engine/compactRowStyles.js +88 -51
- package/dist/components/form/engine/compactRowStyles.js.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.d.ts +1 -0
- package/dist/components/form/engine/compactToolbarContext.d.ts.map +1 -1
- package/dist/components/form/engine/compactToolbarContext.js.map +1 -1
- package/dist/components/form/engine/optionActions.d.ts +28 -0
- package/dist/components/form/engine/optionActions.d.ts.map +1 -0
- package/dist/components/form/engine/optionActions.js +18 -0
- package/dist/components/form/engine/optionActions.js.map +1 -0
- package/dist/components/form/engine/readFirst.d.ts +63 -2
- package/dist/components/form/engine/readFirst.d.ts.map +1 -1
- package/dist/components/form/engine/readFirst.js +121 -6
- package/dist/components/form/engine/readFirst.js.map +1 -1
- package/dist/components/form/engine/rendererTypes.d.ts +35 -0
- package/dist/components/form/engine/rendererTypes.d.ts.map +1 -0
- package/dist/components/form/engine/rendererTypes.js +63 -0
- package/dist/components/form/engine/rendererTypes.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js +94 -0
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantCards.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantCards.js +80 -0
- package/dist/components/form/engine/variants/VariantCards.js.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts +7 -0
- package/dist/components/form/engine/variants/VariantFocus.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantFocus.js +138 -0
- package/dist/components/form/engine/variants/VariantFocus.js.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts +6 -0
- package/dist/components/form/engine/variants/VariantMinimal.d.ts.map +1 -0
- package/dist/components/form/engine/variants/VariantMinimal.js +73 -0
- package/dist/components/form/engine/variants/VariantMinimal.js.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts +13 -0
- package/dist/components/form/engine/variants/focusDemo.d.ts.map +1 -0
- package/dist/components/form/engine/variants/focusDemo.js +139 -0
- package/dist/components/form/engine/variants/focusDemo.js.map +1 -0
- package/dist/components/form/engine/variants/variantModel.d.ts +70 -0
- package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantModel.js +133 -0
- package/dist/components/form/engine/variants/variantModel.js.map +1 -0
- package/dist/components/form/engine/variants/variantParts.d.ts +79 -0
- package/dist/components/form/engine/variants/variantParts.d.ts.map +1 -0
- package/dist/components/form/engine/variants/variantParts.js +191 -0
- package/dist/components/form/engine/variants/variantParts.js.map +1 -0
- package/dist/components/form/expressions/builder/index.d.ts.map +1 -1
- package/dist/components/form/expressions/builder/index.js +13 -2
- package/dist/components/form/expressions/builder/index.js.map +1 -1
- package/dist/components/form/expressions/useExpressions.d.ts.map +1 -1
- package/dist/components/form/expressions/useExpressions.js +4 -1
- package/dist/components/form/expressions/useExpressions.js.map +1 -1
- package/dist/components/form/fields/Field.d.ts.map +1 -1
- package/dist/components/form/fields/Field.js +5 -3
- package/dist/components/form/fields/Field.js.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts +2 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.d.ts.map +1 -1
- package/dist/components/form/fields/allowed-values/AllowedValues.js +8 -26
- package/dist/components/form/fields/allowed-values/AllowedValues.js.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.d.ts +3 -0
- package/dist/components/form/fields/auto/AutoFormField.d.ts.map +1 -1
- package/dist/components/form/fields/auto/AutoFormField.js +42 -8
- package/dist/components/form/fields/auto/AutoFormField.js.map +1 -1
- package/dist/components/form/fields/binary/Binary.d.ts +15 -0
- package/dist/components/form/fields/binary/Binary.d.ts.map +1 -0
- package/dist/components/form/fields/binary/Binary.js +74 -0
- package/dist/components/form/fields/binary/Binary.js.map +1 -0
- package/dist/components/form/fields/boolean/Boolean.js +1 -1
- package/dist/components/form/fields/boolean/Boolean.js.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.d.ts.map +1 -1
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js +5 -4
- package/dist/components/form/fields/multi-select/MultiSelectFormField.js.map +1 -1
- package/dist/components/form/fields/select/Select.d.ts.map +1 -1
- package/dist/components/form/fields/select/Select.js +29 -12
- package/dist/components/form/fields/select/Select.js.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.d.ts +8 -2
- package/dist/components/form/fields/select/SelectCollection.d.ts.map +1 -1
- package/dist/components/form/fields/select/SelectCollection.js +52 -16
- package/dist/components/form/fields/select/SelectCollection.js.map +1 -1
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +2 -0
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/imageLightbox/ImageLightbox.d.ts +21 -0
- package/dist/components/imageLightbox/ImageLightbox.d.ts.map +1 -0
- package/dist/components/imageLightbox/ImageLightbox.js +169 -0
- package/dist/components/imageLightbox/ImageLightbox.js.map +1 -0
- package/dist/components/imageLightbox/index.d.ts +2 -0
- package/dist/components/imageLightbox/index.d.ts.map +1 -0
- package/dist/components/imageLightbox/index.js +18 -0
- package/dist/components/imageLightbox/index.js.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts +33 -0
- package/dist/components/searchFilterBar/SearchFilterBar.d.ts.map +1 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js +76 -0
- package/dist/components/searchFilterBar/SearchFilterBar.js.map +1 -0
- package/dist/components/searchFilterBar/index.d.ts +2 -0
- package/dist/components/searchFilterBar/index.d.ts.map +1 -0
- package/dist/components/searchFilterBar/index.js +18 -0
- package/dist/components/searchFilterBar/index.js.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts +10 -0
- package/dist/components/supportTicket/AttachmentChips.d.ts.map +1 -0
- package/dist/components/supportTicket/AttachmentChips.js +122 -0
- package/dist/components/supportTicket/AttachmentChips.js.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts +43 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.d.ts.map +1 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js +31 -0
- package/dist/components/supportTicket/InterfaceReferenceTags.js.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts +87 -0
- package/dist/components/supportTicket/ReferencePicker.d.ts.map +1 -0
- package/dist/components/supportTicket/ReferencePicker.js +295 -0
- package/dist/components/supportTicket/ReferencePicker.js.map +1 -0
- package/dist/components/supportTicket/TicketHeader.d.ts +86 -0
- package/dist/components/supportTicket/TicketHeader.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketHeader.js +149 -0
- package/dist/components/supportTicket/TicketHeader.js.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts +32 -0
- package/dist/components/supportTicket/TicketMetaTags.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketMetaTags.js +21 -0
- package/dist/components/supportTicket/TicketMetaTags.js.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts +63 -0
- package/dist/components/supportTicket/TicketReplyBox.d.ts.map +1 -0
- package/dist/components/supportTicket/TicketReplyBox.js +115 -0
- package/dist/components/supportTicket/TicketReplyBox.js.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts +37 -0
- package/dist/components/supportTicket/attachmentAccess.d.ts.map +1 -0
- package/dist/components/supportTicket/attachmentAccess.js +132 -0
- package/dist/components/supportTicket/attachmentAccess.js.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts +9 -0
- package/dist/components/supportTicket/fileToAttachment.d.ts.map +1 -0
- package/dist/components/supportTicket/fileToAttachment.js +28 -0
- package/dist/components/supportTicket/fileToAttachment.js.map +1 -0
- package/dist/components/supportTicket/index.d.ts +11 -0
- package/dist/components/supportTicket/index.d.ts.map +1 -0
- package/dist/components/supportTicket/index.js +27 -0
- package/dist/components/supportTicket/index.js.map +1 -0
- package/dist/components/supportTicket/meta.d.ts +71 -0
- package/dist/components/supportTicket/meta.d.ts.map +1 -0
- package/dist/components/supportTicket/meta.js +115 -0
- package/dist/components/supportTicket/meta.js.map +1 -0
- package/dist/components/supportTicket/ticketModel.d.ts +102 -0
- package/dist/components/supportTicket/ticketModel.d.ts.map +1 -0
- package/dist/components/supportTicket/ticketModel.js +3 -0
- package/dist/components/supportTicket/ticketModel.js.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts +52 -0
- package/dist/components/ticketReferences/TicketReferences.d.ts.map +1 -0
- package/dist/components/ticketReferences/TicketReferences.js +492 -0
- package/dist/components/ticketReferences/TicketReferences.js.map +1 -0
- package/dist/components/ticketReferences/index.d.ts +3 -0
- package/dist/components/ticketReferences/index.d.ts.map +1 -0
- package/dist/components/ticketReferences/index.js +6 -0
- package/dist/components/ticketReferences/index.js.map +1 -0
- package/dist/components/ticketThread/TicketThread.d.ts +57 -0
- package/dist/components/ticketThread/TicketThread.d.ts.map +1 -0
- package/dist/components/ticketThread/TicketThread.js +299 -0
- package/dist/components/ticketThread/TicketThread.js.map +1 -0
- package/dist/components/ticketThread/index.d.ts +3 -0
- package/dist/components/ticketThread/index.d.ts.map +1 -0
- package/dist/components/ticketThread/index.js +7 -0
- package/dist/components/ticketThread/index.js.map +1 -0
- package/dist/helpers/common.d.ts +5 -0
- package/dist/helpers/common.d.ts.map +1 -1
- package/dist/helpers/common.js +20 -1
- package/dist/helpers/common.js.map +1 -1
- package/dist/helpers/scrollbar.d.ts +2 -0
- package/dist/helpers/scrollbar.d.ts.map +1 -0
- package/dist/helpers/scrollbar.js +25 -0
- package/dist/helpers/scrollbar.js.map +1 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- 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 +6 -5
- package/src/components/FocusedEditing/index.tsx +17 -2
- package/src/components/composer/Composer.stories.tsx +238 -0
- package/src/components/composer/Composer.tsx +545 -0
- package/src/components/composer/helpers.ts +74 -0
- package/src/components/composer/index.ts +2 -0
- package/src/components/dpqlEditor/DpqlEditor.stories.tsx +74 -2
- package/src/components/form/engine/CompactRow.tsx +561 -283
- package/src/components/form/engine/CompactToolbar.tsx +216 -182
- package/src/components/form/engine/FormEngine.stories.tsx +1736 -162
- package/src/components/form/engine/FormEngine.tsx +886 -170
- package/src/components/form/engine/FormEngineRemote.stories.tsx +377 -0
- package/src/components/form/engine/_structuredData/StructuredDataView.stories.tsx +8 -0
- package/src/components/form/engine/compactRowContext.ts +14 -0
- package/src/components/form/engine/compactRowStyles.ts +273 -146
- package/src/components/form/engine/compactToolbarContext.ts +1 -0
- package/src/components/form/engine/optionActions.ts +40 -0
- package/src/components/form/engine/readFirst.ts +169 -10
- package/src/components/form/engine/rendererTypes.ts +55 -0
- package/src/components/form/engine/variants/FormEngineVariants.stories.tsx +159 -0
- package/src/components/form/engine/variants/VariantCalmTable.tsx +242 -0
- package/src/components/form/engine/variants/VariantCards.tsx +212 -0
- package/src/components/form/engine/variants/VariantFocus.tsx +382 -0
- package/src/components/form/engine/variants/VariantMinimal.tsx +170 -0
- package/src/components/form/engine/variants/focusDemo.ts +145 -0
- package/src/components/form/engine/variants/variantModel.ts +216 -0
- package/src/components/form/engine/variants/variantParts.tsx +313 -0
- package/src/components/form/expressions/ExpressionField.stories.tsx +84 -2
- package/src/components/form/expressions/builder/ExpressionBuilder.stories.tsx +213 -19
- package/src/components/form/expressions/builder/index.tsx +12 -1
- package/src/components/form/expressions/useExpressions.ts +4 -1
- package/src/components/form/fields/Field.stories.tsx +40 -0
- package/src/components/form/fields/Field.tsx +10 -1
- package/src/components/form/fields/allowed-values/AllowedValues.tsx +13 -36
- package/src/components/form/fields/array/ArrayAutoField.stories.tsx +64 -0
- package/src/components/form/fields/auto/AutoFormField.stories.tsx +146 -2
- package/src/components/form/fields/auto/AutoFormField.tsx +42 -1
- package/src/components/form/fields/binary/Binary.stories.tsx +122 -0
- package/src/components/form/fields/binary/Binary.tsx +81 -0
- package/src/components/form/fields/boolean/Boolean.stories.tsx +58 -0
- package/src/components/form/fields/boolean/Boolean.tsx +1 -1
- package/src/components/form/fields/byte-size/ByteSize.stories.tsx +24 -0
- package/src/components/form/fields/color/Color.stories.tsx +30 -0
- package/src/components/form/fields/cron/Cron.stories.tsx +8 -0
- package/src/components/form/fields/date/Date.stories.tsx +22 -0
- package/src/components/form/fields/file/File.stories.tsx +48 -0
- package/src/components/form/fields/long-string/LongString.stories.tsx +24 -0
- package/src/components/form/fields/markdown/Markdown.stories.tsx +8 -0
- package/src/components/form/fields/multi-select/MultiSelectFormField.tsx +14 -10
- package/src/components/form/fields/number/Number.stories.tsx +31 -0
- package/src/components/form/fields/object/Object.stories.tsx +88 -0
- package/src/components/form/fields/radio-group/RadioGroup.stories.tsx +24 -0
- package/src/components/form/fields/rich-text/RichText.stories.tsx +23 -0
- package/src/components/form/fields/schema-definition/SchemaDefinitionField.stories.tsx +72 -0
- package/src/components/form/fields/select/Select.stories.tsx +108 -0
- package/src/components/form/fields/select/Select.tsx +38 -12
- package/src/components/form/fields/select/SelectCollection.tsx +78 -17
- package/src/components/form/fields/string/String.stories.tsx +16 -0
- package/src/components/form/fields/template/TemplateField.stories.tsx +160 -0
- package/src/components/form/fields/url/Url.stories.tsx +24 -0
- package/src/components/form/index.tsx +2 -0
- package/src/components/imageLightbox/ImageLightbox.stories.tsx +238 -0
- package/src/components/imageLightbox/ImageLightbox.tsx +236 -0
- package/src/components/imageLightbox/index.ts +1 -0
- package/src/components/log/Log.stories.tsx +96 -0
- package/src/components/menu/Menu.stories.tsx +54 -0
- package/src/components/qonsoleSmartInput/QonsoleSmartInput.stories.tsx +83 -37
- package/src/components/searchFilterBar/SearchFilterBar.tsx +131 -0
- package/src/components/searchFilterBar/index.ts +1 -0
- package/src/components/smartEditor/SmartEditor.stories.tsx +32 -0
- package/src/components/supportTicket/AttachmentChips.stories.tsx +171 -0
- package/src/components/supportTicket/AttachmentChips.tsx +147 -0
- package/src/components/supportTicket/InterfaceReferenceTags.stories.tsx +171 -0
- package/src/components/supportTicket/InterfaceReferenceTags.tsx +96 -0
- package/src/components/supportTicket/ReferencePicker.stories.tsx +519 -0
- package/src/components/supportTicket/ReferencePicker.tsx +563 -0
- package/src/components/supportTicket/TicketConversation.stories.tsx +121 -0
- package/src/components/supportTicket/TicketHeader.stories.tsx +183 -0
- package/src/components/supportTicket/TicketHeader.tsx +247 -0
- package/src/components/supportTicket/TicketMetaTags.stories.tsx +103 -0
- package/src/components/supportTicket/TicketMetaTags.tsx +85 -0
- package/src/components/supportTicket/TicketReplyBox.stories.tsx +351 -0
- package/src/components/supportTicket/TicketReplyBox.tsx +146 -0
- package/src/components/supportTicket/attachmentAccess.ts +97 -0
- package/src/components/supportTicket/fileToAttachment.ts +24 -0
- package/src/components/supportTicket/index.ts +10 -0
- package/src/components/supportTicket/meta.ts +185 -0
- package/src/components/supportTicket/ticketModel.ts +131 -0
- package/src/components/ticketReferences/TicketReferences.stories.tsx +315 -0
- package/src/components/ticketReferences/TicketReferences.tsx +886 -0
- package/src/components/ticketReferences/index.ts +2 -0
- package/src/components/ticketThread/TicketThread.stories.tsx +828 -0
- package/src/components/ticketThread/TicketThread.tsx +704 -0
- package/src/components/ticketThread/index.ts +6 -0
- package/src/helpers/common.ts +19 -0
- package/src/helpers/scrollbar.ts +32 -0
- package/src/hooks/useFetch/useFetch.stories.tsx +30 -0
- package/src/hooks/useStorage/useStorage.stories.tsx +82 -0
- package/src/hooks/useWebSocket/useWebsocket.stories.tsx +127 -1
- package/src/index.tsx +8 -1
- package/src/providers/StorageProvider.tsx +30 -4
- package/src/stores/currentUser/currentUser.stories.tsx +49 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { StoryMeta } from '../../types';
|
|
5
|
+
import { IImageLightboxImage, ImageLightbox } from './ImageLightbox';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The shared full-image viewer, on `ReqoreModal`. Opened by the ticket thread (an image
|
|
9
|
+
* on a message) and the References tab (the screenshot grid) alike. Pages across a set,
|
|
10
|
+
* click-to-zoom, download; the shell/backdrop/Esc are the modal's.
|
|
11
|
+
*/
|
|
12
|
+
const meta = {
|
|
13
|
+
component: ImageLightbox,
|
|
14
|
+
title: 'Components/ImageLightbox',
|
|
15
|
+
} as StoryMeta<typeof ImageLightbox>;
|
|
16
|
+
export default meta;
|
|
17
|
+
type Story = StoryObj<typeof meta>;
|
|
18
|
+
|
|
19
|
+
const shot = (label: string, hue: number): string =>
|
|
20
|
+
`data:image/svg+xml,${encodeURIComponent(
|
|
21
|
+
`<svg xmlns='http://www.w3.org/2000/svg' width='640' height='400'>` +
|
|
22
|
+
`<rect width='640' height='400' fill='hsl(${hue},38%,16%)'/>` +
|
|
23
|
+
`<text x='24' y='210' fill='white' font-family='sans-serif' font-size='28' opacity='0.7'>${label}</text>` +
|
|
24
|
+
`</svg>`
|
|
25
|
+
)}`;
|
|
26
|
+
|
|
27
|
+
const IMAGES: IImageLightboxImage[] = [
|
|
28
|
+
{ attachment_id: 'i1', filename: 'canvas-error.png' },
|
|
29
|
+
{ attachment_id: 'i2', filename: 'console.png' },
|
|
30
|
+
{ attachment_id: 'i3', filename: 'network.png' },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/** Open on a set of three, so the paging chrome (`1 / 3`, prev/next) shows. */
|
|
34
|
+
export const Paging: Story = {
|
|
35
|
+
parameters: {
|
|
36
|
+
docs: {
|
|
37
|
+
description: {
|
|
38
|
+
story:
|
|
39
|
+
"Renders the lightbox open on a set of three images, so the paging chrome ('1 / 3', prev/next) shows, and arrow keys page through the set.",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
render: function Render() {
|
|
44
|
+
const [index, setIndex] = useState<number | null>(0);
|
|
45
|
+
if (index === null) {
|
|
46
|
+
return <div>closed</div>;
|
|
47
|
+
}
|
|
48
|
+
return (
|
|
49
|
+
<ImageLightbox
|
|
50
|
+
images={IMAGES}
|
|
51
|
+
index={index}
|
|
52
|
+
onIndex={setIndex}
|
|
53
|
+
onClose={() => setIndex(null)}
|
|
54
|
+
fetchAttachmentUrl={async (id) => shot(id, id === 'i1' ? 265 : id === 'i2' ? 205 : 150)}
|
|
55
|
+
onDownload={fn()}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
},
|
|
59
|
+
async play() {
|
|
60
|
+
// the modal portals to the body, so query document-wide
|
|
61
|
+
await waitFor(() => expect(document.querySelector('[role="dialog"]')).not.toBeNull());
|
|
62
|
+
const dialog = within(document.body);
|
|
63
|
+
await expect(await dialog.findByText('canvas-error.png')).toBeInTheDocument();
|
|
64
|
+
await expect(dialog.getByText('1 / 3')).toBeInTheDocument();
|
|
65
|
+
// page forward via the keyboard (the action buttons are icon-only)
|
|
66
|
+
await userEvent.keyboard('{ArrowRight}');
|
|
67
|
+
await waitFor(() => expect(dialog.getByText('2 / 3')).toBeInTheDocument());
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Regression guard for the "screenshot preview stuck loading" bug.
|
|
73
|
+
*
|
|
74
|
+
* Consumers pass `fetchAttachmentUrl` as an inline arrow — a new identity every render —
|
|
75
|
+
* and re-render for reasons unrelated to the lightbox. Here the parent ticks on a timer
|
|
76
|
+
* (handing a fresh fetcher each render) while the fetch is deliberately slow, so those
|
|
77
|
+
* re-renders land WHILE the image is still loading. When the fetch effect depended on
|
|
78
|
+
* `fetchAttachmentUrl`, each re-render tore down the in-flight fetch and the `requested`
|
|
79
|
+
* guard blocked a retry — the spinner hung forever. The image resolving proves the effect
|
|
80
|
+
* now depends on `id` alone. Removing that fix makes this story time out.
|
|
81
|
+
*/
|
|
82
|
+
export const ResolvesDespiteParentReRenders: Story = {
|
|
83
|
+
parameters: {
|
|
84
|
+
docs: {
|
|
85
|
+
description: {
|
|
86
|
+
story:
|
|
87
|
+
'Renders the lightbox while the parent re-renders on a timer with a deliberately slow fetch — a regression guard proving the image still resolves instead of the spinner hanging forever.',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
render: function Render() {
|
|
92
|
+
const [index, setIndex] = useState<number | null>(0);
|
|
93
|
+
const [, setTick] = useState(0);
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const timer = setInterval(() => setTick((n) => n + 1), 30);
|
|
96
|
+
return () => clearInterval(timer);
|
|
97
|
+
}, []);
|
|
98
|
+
if (index === null) {
|
|
99
|
+
return <div>closed</div>;
|
|
100
|
+
}
|
|
101
|
+
return (
|
|
102
|
+
<ImageLightbox
|
|
103
|
+
images={[IMAGES[0]]}
|
|
104
|
+
index={index}
|
|
105
|
+
onIndex={setIndex}
|
|
106
|
+
onClose={() => setIndex(null)}
|
|
107
|
+
// a brand-new arrow every render, and a slow fetch so the re-renders overlap it
|
|
108
|
+
fetchAttachmentUrl={async (id) => {
|
|
109
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
110
|
+
return shot(id, 265);
|
|
111
|
+
}}
|
|
112
|
+
onDownload={fn()}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
async play() {
|
|
117
|
+
await expect(await within(document.body).findByText('canvas-error.png')).toBeInTheDocument();
|
|
118
|
+
// the image resolves and paints — it never would while the spinner was hung
|
|
119
|
+
await waitFor(
|
|
120
|
+
() => expect(document.body.querySelector('img[alt="canvas-error.png"]')).not.toBeNull(),
|
|
121
|
+
{ timeout: 3000 }
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Renaming from inside the preview. The pencil (only offered when `onRename` is wired)
|
|
128
|
+
* swaps in an input row; Enter commits through the host, which owns the actual rename —
|
|
129
|
+
* here the harness updates its image list, so the modal header shows the new name.
|
|
130
|
+
* Esc cancels the edit WITHOUT closing the modal. Discriminating: remove the
|
|
131
|
+
* `onRename` plumbing and the pencil never renders / the title never changes.
|
|
132
|
+
*/
|
|
133
|
+
export const Renamable: Story = {
|
|
134
|
+
parameters: {
|
|
135
|
+
docs: {
|
|
136
|
+
description: {
|
|
137
|
+
story:
|
|
138
|
+
'Renders the lightbox with onRename wired — the header pencil opens an inline input; Enter commits the new name through the host and Esc cancels without closing the modal.',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
render: function Render() {
|
|
143
|
+
const [index, setIndex] = useState<number | null>(0);
|
|
144
|
+
const [images, setImages] = useState<IImageLightboxImage[]>([IMAGES[0]]);
|
|
145
|
+
if (index === null) {
|
|
146
|
+
return <div>closed</div>;
|
|
147
|
+
}
|
|
148
|
+
return (
|
|
149
|
+
<ImageLightbox
|
|
150
|
+
images={images}
|
|
151
|
+
index={index}
|
|
152
|
+
onIndex={setIndex}
|
|
153
|
+
onClose={() => setIndex(null)}
|
|
154
|
+
fetchAttachmentUrl={async (id) => shot(id, 265)}
|
|
155
|
+
onRename={(id, name) =>
|
|
156
|
+
setImages((current) =>
|
|
157
|
+
current.map((img) => (img.attachment_id === id ? { ...img, filename: name } : img))
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
},
|
|
163
|
+
async play() {
|
|
164
|
+
const dialog = within(document.body);
|
|
165
|
+
await expect(await dialog.findByText('canvas-error.png')).toBeInTheDocument();
|
|
166
|
+
|
|
167
|
+
const pencil = await waitFor(() => {
|
|
168
|
+
const el = document.body.querySelector<HTMLButtonElement>('.imagelightbox-rename');
|
|
169
|
+
if (!el) {
|
|
170
|
+
throw new Error('rename action not rendered');
|
|
171
|
+
}
|
|
172
|
+
return el;
|
|
173
|
+
});
|
|
174
|
+
await userEvent.click(pencil);
|
|
175
|
+
|
|
176
|
+
// Esc cancels the edit but keeps the modal open
|
|
177
|
+
const input = await waitFor(() => {
|
|
178
|
+
const el = document.body.querySelector<HTMLInputElement>('.reqore-modal input');
|
|
179
|
+
if (!el) {
|
|
180
|
+
throw new Error('rename input not shown');
|
|
181
|
+
}
|
|
182
|
+
return el;
|
|
183
|
+
});
|
|
184
|
+
await expect(input).toHaveValue('canvas-error.png'); // pre-filled with the current name
|
|
185
|
+
await userEvent.keyboard('{Escape}');
|
|
186
|
+
await waitFor(() =>
|
|
187
|
+
expect(document.body.querySelector('.reqore-modal input')).toBeNull()
|
|
188
|
+
);
|
|
189
|
+
await expect(document.querySelector('[role="dialog"]')).not.toBeNull();
|
|
190
|
+
|
|
191
|
+
// now rename for real: pencil → clear → type → Enter → header carries the new name
|
|
192
|
+
await userEvent.click(document.body.querySelector<HTMLButtonElement>('.imagelightbox-rename')!);
|
|
193
|
+
const input2 = await waitFor(() => {
|
|
194
|
+
const el = document.body.querySelector<HTMLInputElement>('.reqore-modal input');
|
|
195
|
+
if (!el) {
|
|
196
|
+
throw new Error('rename input not shown (second open)');
|
|
197
|
+
}
|
|
198
|
+
return el;
|
|
199
|
+
});
|
|
200
|
+
await userEvent.clear(input2);
|
|
201
|
+
await userEvent.type(input2, 'mapper-crash.png');
|
|
202
|
+
await userEvent.keyboard('{Enter}');
|
|
203
|
+
await waitFor(() => expect(dialog.getByText('mapper-crash.png')).toBeInTheDocument());
|
|
204
|
+
await expect(dialog.queryByText('canvas-error.png')).not.toBeInTheDocument();
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/** A single image: no paging chrome. */
|
|
209
|
+
export const Single: Story = {
|
|
210
|
+
parameters: {
|
|
211
|
+
docs: {
|
|
212
|
+
description: {
|
|
213
|
+
story: 'Renders the lightbox on a single image, so no paging chrome appears.',
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
render: function Render() {
|
|
218
|
+
const [index, setIndex] = useState<number | null>(0);
|
|
219
|
+
if (index === null) {
|
|
220
|
+
return <div>closed</div>;
|
|
221
|
+
}
|
|
222
|
+
return (
|
|
223
|
+
<ImageLightbox
|
|
224
|
+
images={[IMAGES[0]]}
|
|
225
|
+
index={index}
|
|
226
|
+
onIndex={setIndex}
|
|
227
|
+
onClose={() => setIndex(null)}
|
|
228
|
+
fetchAttachmentUrl={async (id) => shot(id, 265)}
|
|
229
|
+
onDownload={fn()}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
232
|
+
},
|
|
233
|
+
async play() {
|
|
234
|
+
const dialog = within(document.body);
|
|
235
|
+
await expect(await dialog.findByText('canvas-error.png')).toBeInTheDocument();
|
|
236
|
+
await expect(dialog.queryByText('1 / 1')).not.toBeInTheDocument();
|
|
237
|
+
},
|
|
238
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReqoreInput,
|
|
3
|
+
ReqoreModal,
|
|
4
|
+
ReqoreSpan,
|
|
5
|
+
ReqoreSpinner,
|
|
6
|
+
useLatestZIndex,
|
|
7
|
+
} from '@qoretechnologies/reqore';
|
|
8
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
9
|
+
import styled from 'styled-components';
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* The shared full-image viewer: a `ReqoreModal` holding one image at a time, with
|
|
13
|
+
* click-to-zoom, keyboard/arrow paging across a set, and a download action. Both the
|
|
14
|
+
* ticket thread (an image attachment on a message) and the References tab (the ticket's
|
|
15
|
+
* screenshot grid) open this, so there is one lightbox rather than a Modal-backed one
|
|
16
|
+
* here and a hand-rolled overlay there.
|
|
17
|
+
*
|
|
18
|
+
* reqore has no image-viewer primitive, so the zoom stage below is bespoke — but the
|
|
19
|
+
* shell, backdrop, focus trap, Esc-to-close and header/actions are all `ReqoreModal`.
|
|
20
|
+
* When reqore gains a real viewer this collapses onto it. It fetches the shown image's
|
|
21
|
+
* bytes itself (caching per id), so a consumer only has to hand it `fetchAttachmentUrl`.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// Compact rename row above the stage while editing — the modal's `label` slot is
|
|
25
|
+
// string-only, so the input can't live in the header itself.
|
|
26
|
+
const RenameRow = styled.div`
|
|
27
|
+
margin-bottom: 8px;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const Stage = styled.div<{ $zoom: boolean }>`
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
min-height: 40vh;
|
|
35
|
+
max-height: 74vh;
|
|
36
|
+
overflow: auto;
|
|
37
|
+
background: rgba(0, 0, 0, 0.25);
|
|
38
|
+
border-radius: 8px;
|
|
39
|
+
|
|
40
|
+
img {
|
|
41
|
+
display: block;
|
|
42
|
+
cursor: ${({ $zoom }) => ($zoom ? 'zoom-out' : 'zoom-in')};
|
|
43
|
+
${({ $zoom }) =>
|
|
44
|
+
$zoom
|
|
45
|
+
? 'max-width: none; max-height: none;'
|
|
46
|
+
: 'max-width: 100%; max-height: 74vh; object-fit: contain;'}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
export interface IImageLightboxImage {
|
|
51
|
+
attachment_id: string;
|
|
52
|
+
filename: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface IImageLightboxProps {
|
|
56
|
+
/** the set the viewer pages across — one image is a set of one (no paging chrome) */
|
|
57
|
+
images: IImageLightboxImage[];
|
|
58
|
+
/** which image is shown */
|
|
59
|
+
index: number;
|
|
60
|
+
onIndex: (index: number) => void;
|
|
61
|
+
onClose: () => void;
|
|
62
|
+
/** resolve an image's bytes as an object URL (the consumer's authed endpoint) */
|
|
63
|
+
fetchAttachmentUrl: (attachmentId: string) => Promise<string>;
|
|
64
|
+
onDownload?: (attachmentId: string, filename: string) => void;
|
|
65
|
+
/** Rename the shown image. Presence of the callback IS the permission — a staged
|
|
66
|
+
* (composer) set passes it, server-side attachment sets simply don't, so the
|
|
67
|
+
* thread / References viewers stay rename-free without any extra flag. */
|
|
68
|
+
onRename?: (attachmentId: string, newName: string) => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const ImageLightbox = ({
|
|
72
|
+
images,
|
|
73
|
+
index,
|
|
74
|
+
onIndex,
|
|
75
|
+
onClose,
|
|
76
|
+
fetchAttachmentUrl,
|
|
77
|
+
onDownload,
|
|
78
|
+
onRename,
|
|
79
|
+
}: IImageLightboxProps) => {
|
|
80
|
+
const [zoom, setZoom] = useState<boolean>(false);
|
|
81
|
+
// inline rename state: editing toggles the input row; draftName is the working value
|
|
82
|
+
const [editing, setEditing] = useState<boolean>(false);
|
|
83
|
+
const [draftName, setDraftName] = useState<string>('');
|
|
84
|
+
// url per attachment: undefined = not fetched, null = fetch failed, string = ready
|
|
85
|
+
const [urls, setUrls] = useState<Record<string, string | null>>({});
|
|
86
|
+
const requested = useRef<Set<string>>(new Set());
|
|
87
|
+
// Render above everything currently on screen: the ticket composer that can open this
|
|
88
|
+
// lightbox floats at its own climbed z-index (also useLatestZIndex), so a modal on a fixed
|
|
89
|
+
// z-index ends up BEHIND it. Climbing past the current top keeps the viewer on top wherever
|
|
90
|
+
// it's opened from.
|
|
91
|
+
const zIndex = useLatestZIndex();
|
|
92
|
+
|
|
93
|
+
const image = images[index];
|
|
94
|
+
const id = image?.attachment_id;
|
|
95
|
+
const url = id ? urls[id] : undefined;
|
|
96
|
+
const multiple = images.length > 1;
|
|
97
|
+
|
|
98
|
+
// Keep the latest fetcher in a ref so the fetch effect can depend on `id` ALONE.
|
|
99
|
+
// Consumers routinely pass `fetchAttachmentUrl` as an inline arrow — a new identity
|
|
100
|
+
// every render. If the effect depended on it, each parent re-render would re-run the
|
|
101
|
+
// effect: the previous run's cleanup fired first, so its in-flight fetch resolved with
|
|
102
|
+
// nothing to write to, and because `requested` already held the id nothing re-fetched.
|
|
103
|
+
// Net result: the spinner hung forever. Depending on `id` only breaks that cycle.
|
|
104
|
+
const fetchRef = useRef(fetchAttachmentUrl);
|
|
105
|
+
fetchRef.current = fetchAttachmentUrl;
|
|
106
|
+
// True only while mounted: a fetch that resolves after unmount is dropped, but one that
|
|
107
|
+
// resolves after a benign re-render still caches — the distinction a per-run flag missed.
|
|
108
|
+
const mounted = useRef(true);
|
|
109
|
+
useEffect(
|
|
110
|
+
() => () => {
|
|
111
|
+
mounted.current = false;
|
|
112
|
+
},
|
|
113
|
+
[]
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// fetch the shown image's bytes once per id; `requested` guards against re-fetching a
|
|
117
|
+
// page you've already visited
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
if (!id || requested.current.has(id)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
requested.current.add(id);
|
|
123
|
+
fetchRef.current(id)
|
|
124
|
+
.then((resolved) => mounted.current && setUrls((current) => ({ ...current, [id]: resolved })))
|
|
125
|
+
.catch(() => mounted.current && setUrls((current) => ({ ...current, [id]: null })));
|
|
126
|
+
return undefined;
|
|
127
|
+
}, [id]);
|
|
128
|
+
|
|
129
|
+
const step = useCallback(
|
|
130
|
+
(delta: number) => {
|
|
131
|
+
setZoom(false);
|
|
132
|
+
// paging abandons an in-flight rename — the draft belonged to the image left behind
|
|
133
|
+
setEditing(false);
|
|
134
|
+
onIndex((index + delta + images.length) % images.length);
|
|
135
|
+
},
|
|
136
|
+
[index, images.length, onIndex]
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!multiple || editing) {
|
|
141
|
+
// while renaming, arrow keys belong to the text input, not the pager
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
const handler = (event: KeyboardEvent) => {
|
|
145
|
+
if (event.key === 'ArrowRight') {
|
|
146
|
+
step(1);
|
|
147
|
+
} else if (event.key === 'ArrowLeft') {
|
|
148
|
+
step(-1);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
window.addEventListener('keydown', handler);
|
|
152
|
+
return () => window.removeEventListener('keydown', handler);
|
|
153
|
+
}, [step, multiple, editing]);
|
|
154
|
+
|
|
155
|
+
const startRename = useCallback(() => {
|
|
156
|
+
setDraftName(images[index]?.filename ?? '');
|
|
157
|
+
setEditing(true);
|
|
158
|
+
}, [images, index]);
|
|
159
|
+
|
|
160
|
+
const commitRename = useCallback(() => {
|
|
161
|
+
const current = images[index];
|
|
162
|
+
const name = draftName.trim();
|
|
163
|
+
if (onRename && current && name && name !== current.filename) {
|
|
164
|
+
onRename(current.attachment_id, name);
|
|
165
|
+
}
|
|
166
|
+
setEditing(false);
|
|
167
|
+
}, [draftName, images, index, onRename]);
|
|
168
|
+
|
|
169
|
+
if (!image) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<ReqoreModal
|
|
175
|
+
isOpen
|
|
176
|
+
onClose={onClose}
|
|
177
|
+
customZIndex={zIndex}
|
|
178
|
+
blur={3}
|
|
179
|
+
icon='ImageLine'
|
|
180
|
+
label={image.filename}
|
|
181
|
+
badge={multiple ? `${index + 1} / ${images.length}` : undefined}
|
|
182
|
+
actions={[
|
|
183
|
+
{ icon: 'ArrowLeftSLine', tooltip: 'Previous', onClick: () => step(-1), show: multiple },
|
|
184
|
+
{ icon: 'ArrowRightSLine', tooltip: 'Next', onClick: () => step(1), show: multiple },
|
|
185
|
+
{
|
|
186
|
+
icon: 'Edit2Line',
|
|
187
|
+
tooltip: 'Rename',
|
|
188
|
+
className: 'imagelightbox-rename',
|
|
189
|
+
onClick: startRename,
|
|
190
|
+
show: !!onRename && !editing,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
icon: 'DownloadLine',
|
|
194
|
+
tooltip: 'Download',
|
|
195
|
+
onClick: () => onDownload?.(image.attachment_id, image.filename),
|
|
196
|
+
show: !!onDownload,
|
|
197
|
+
},
|
|
198
|
+
]}
|
|
199
|
+
>
|
|
200
|
+
{editing ? (
|
|
201
|
+
<RenameRow>
|
|
202
|
+
<ReqoreInput
|
|
203
|
+
value={draftName}
|
|
204
|
+
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
|
205
|
+
setDraftName(event.target.value)
|
|
206
|
+
}
|
|
207
|
+
onBlur={commitRename}
|
|
208
|
+
onKeyDown={(event: React.KeyboardEvent) => {
|
|
209
|
+
if (event.key === 'Enter') {
|
|
210
|
+
commitRename();
|
|
211
|
+
} else if (event.key === 'Escape') {
|
|
212
|
+
// cancel the edit only — don't let the modal's own Esc-to-close see it
|
|
213
|
+
event.stopPropagation();
|
|
214
|
+
setEditing(false);
|
|
215
|
+
}
|
|
216
|
+
}}
|
|
217
|
+
size='small'
|
|
218
|
+
fluid
|
|
219
|
+
autoFocus
|
|
220
|
+
/>
|
|
221
|
+
</RenameRow>
|
|
222
|
+
) : null}
|
|
223
|
+
<Stage $zoom={zoom}>
|
|
224
|
+
{url ? (
|
|
225
|
+
<img src={url} alt={image.filename} onClick={() => setZoom((z) => !z)} />
|
|
226
|
+
) : url === null ? (
|
|
227
|
+
<ReqoreSpan effect={{ opacity: 0.6 }}>Preview unavailable — use Download.</ReqoreSpan>
|
|
228
|
+
) : (
|
|
229
|
+
<ReqoreSpinner iconColor='info' size='big'>
|
|
230
|
+
Loading…
|
|
231
|
+
</ReqoreSpinner>
|
|
232
|
+
)}
|
|
233
|
+
</Stage>
|
|
234
|
+
</ReqoreModal>
|
|
235
|
+
);
|
|
236
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ImageLightbox';
|
|
@@ -93,6 +93,14 @@ export default meta;
|
|
|
93
93
|
export type Story = StoryObj<typeof meta>;
|
|
94
94
|
|
|
95
95
|
export const Basic: Story = {
|
|
96
|
+
parameters: {
|
|
97
|
+
docs: {
|
|
98
|
+
description: {
|
|
99
|
+
story:
|
|
100
|
+
'Renders ReqraftLog wired to a mock WebSocket that emits a stream of test log lines — the messages accumulate in the log panel as they arrive.',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
96
104
|
play: async () => {
|
|
97
105
|
await testsWaitForText('This is a message with a link: https://www.qoretechnologies.com');
|
|
98
106
|
},
|
|
@@ -103,6 +111,14 @@ export const Filterable: Story = {
|
|
|
103
111
|
args: {
|
|
104
112
|
filterable: true,
|
|
105
113
|
},
|
|
114
|
+
parameters: {
|
|
115
|
+
docs: {
|
|
116
|
+
description: {
|
|
117
|
+
story:
|
|
118
|
+
'Renders ReqraftLog with the filter input enabled. Typing "error" narrows the visible list to the single ERROR message.',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
106
122
|
play: async (args) => {
|
|
107
123
|
await Basic.play(args);
|
|
108
124
|
await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'error' } });
|
|
@@ -115,6 +131,14 @@ export const WithTimestamps: Story = {
|
|
|
115
131
|
args: {
|
|
116
132
|
showTimestamps: true,
|
|
117
133
|
},
|
|
134
|
+
parameters: {
|
|
135
|
+
docs: {
|
|
136
|
+
description: {
|
|
137
|
+
story:
|
|
138
|
+
'Renders ReqraftLog with per-message timestamps enabled — each streamed line is prefixed with its arrival time.',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
118
142
|
};
|
|
119
143
|
|
|
120
144
|
export const WithDefaultMessages: Story = {
|
|
@@ -126,6 +150,14 @@ export const WithDefaultMessages: Story = {
|
|
|
126
150
|
{ message: 'This is another default message', timestamp: '08:00:00.000' },
|
|
127
151
|
],
|
|
128
152
|
},
|
|
153
|
+
parameters: {
|
|
154
|
+
docs: {
|
|
155
|
+
description: {
|
|
156
|
+
story:
|
|
157
|
+
'Renders ReqraftLog seeded with two default messages — those lines appear before any messages arrive from the socket.',
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
129
161
|
};
|
|
130
162
|
|
|
131
163
|
export const WithCopyableMessages: Story = {
|
|
@@ -133,6 +165,14 @@ export const WithCopyableMessages: Story = {
|
|
|
133
165
|
args: {
|
|
134
166
|
allowMessageCopy: true,
|
|
135
167
|
},
|
|
168
|
+
parameters: {
|
|
169
|
+
docs: {
|
|
170
|
+
description: {
|
|
171
|
+
story:
|
|
172
|
+
'Renders ReqraftLog with allowMessageCopy — each message row exposes a copy action next to its content.',
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
136
176
|
play: async (args) => {
|
|
137
177
|
await Basic.play(args);
|
|
138
178
|
},
|
|
@@ -144,6 +184,14 @@ export const WithDeletableMessages: Story = {
|
|
|
144
184
|
allowMessageCopy: true,
|
|
145
185
|
allowMessageDeletion: true,
|
|
146
186
|
},
|
|
187
|
+
parameters: {
|
|
188
|
+
docs: {
|
|
189
|
+
description: {
|
|
190
|
+
story:
|
|
191
|
+
'Renders ReqraftLog with copy and delete actions on every message. Clicking a message\'s delete action removes just that row from the log.',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
147
195
|
play: async (args) => {
|
|
148
196
|
await Basic.play(args);
|
|
149
197
|
await testsClickButton({ selector: '.reqraft-log-delete-message', nth: 4 });
|
|
@@ -155,6 +203,14 @@ export const Pause: Story = {
|
|
|
155
203
|
args: {
|
|
156
204
|
allowMessageDeletion: true,
|
|
157
205
|
},
|
|
206
|
+
parameters: {
|
|
207
|
+
docs: {
|
|
208
|
+
description: {
|
|
209
|
+
story:
|
|
210
|
+
'Renders ReqraftLog while messages stream in. Clicking the pause/resume action halts appending new messages until it is clicked again.',
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
158
214
|
play: async () => {
|
|
159
215
|
await testsWaitForText('This is another message');
|
|
160
216
|
await testsClickButton({ selector: '.reqraft-log-pause-resume' });
|
|
@@ -166,6 +222,14 @@ export const Clear: Story = {
|
|
|
166
222
|
args: {
|
|
167
223
|
allowMessageDeletion: true,
|
|
168
224
|
},
|
|
225
|
+
parameters: {
|
|
226
|
+
docs: {
|
|
227
|
+
description: {
|
|
228
|
+
story:
|
|
229
|
+
'Renders ReqraftLog with messages streamed in. Clicking the clear action empties the log and the "No messages" empty state is shown.',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
169
233
|
play: async (args) => {
|
|
170
234
|
await Basic.play(args);
|
|
171
235
|
await testsClickButton({ selector: '.reqraft-log-clear' });
|
|
@@ -192,6 +256,14 @@ export const FormattedMessages: Story = {
|
|
|
192
256
|
return { message, ...rest };
|
|
193
257
|
},
|
|
194
258
|
},
|
|
259
|
+
parameters: {
|
|
260
|
+
docs: {
|
|
261
|
+
description: {
|
|
262
|
+
story:
|
|
263
|
+
'Renders ReqraftLog with a messageFormatter that assigns intents by content — WARNING lines colour warning, ERROR lines danger and ALL-CAPS lines info.',
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
},
|
|
195
267
|
play: async (args) => {
|
|
196
268
|
await Basic.play(args);
|
|
197
269
|
},
|
|
@@ -203,6 +275,14 @@ export const CanSendMessages: Story = {
|
|
|
203
275
|
canSendMessages: true,
|
|
204
276
|
showTimestamps: true,
|
|
205
277
|
},
|
|
278
|
+
parameters: {
|
|
279
|
+
docs: {
|
|
280
|
+
description: {
|
|
281
|
+
story:
|
|
282
|
+
'Renders ReqraftLog with canSendMessages — an input at the bottom lets the operator send a message back through the socket alongside the incoming stream.',
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
206
286
|
play: async (args) => {
|
|
207
287
|
await Basic.play(args);
|
|
208
288
|
},
|
|
@@ -214,6 +294,14 @@ export const WithAutoScroll: Story = {
|
|
|
214
294
|
autoScroll: true,
|
|
215
295
|
style: { height: '200px' },
|
|
216
296
|
},
|
|
297
|
+
parameters: {
|
|
298
|
+
docs: {
|
|
299
|
+
description: {
|
|
300
|
+
story:
|
|
301
|
+
'Renders ReqraftLog with autoScroll in a fixed 200px viewport — the log stays pinned to the newest message as new lines arrive.',
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
},
|
|
217
305
|
play: async (args) => {
|
|
218
306
|
await Basic.play(args);
|
|
219
307
|
},
|
|
@@ -225,6 +313,14 @@ export const WithAutoScrollAndManualScroll: Story = {
|
|
|
225
313
|
autoScroll: true,
|
|
226
314
|
style: { height: '200px' },
|
|
227
315
|
},
|
|
316
|
+
parameters: {
|
|
317
|
+
docs: {
|
|
318
|
+
description: {
|
|
319
|
+
story:
|
|
320
|
+
'Renders ReqraftLog with autoScroll while the operator scrolls the panel manually — auto-scroll pauses so the operator can inspect older messages without being yanked back to the tail.',
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
228
324
|
play: async (args) => {
|
|
229
325
|
await WithAutoScroll.play(args);
|
|
230
326
|
await sleep(3000);
|