@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
|
@@ -27,6 +27,14 @@ export const Basic: Story = {
|
|
|
27
27
|
args: {
|
|
28
28
|
menu: typedMenu,
|
|
29
29
|
},
|
|
30
|
+
parameters: {
|
|
31
|
+
docs: {
|
|
32
|
+
description: {
|
|
33
|
+
story:
|
|
34
|
+
'Renders ReqraftMenu with the mock Qorus navigation — sections and items appear in the sidebar with no active path highlighted.',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
30
38
|
play: async () => {
|
|
31
39
|
await testsWaitForText('Interfaces & More');
|
|
32
40
|
},
|
|
@@ -36,6 +44,14 @@ export const ActivePath: Story = {
|
|
|
36
44
|
path: '/Interfaces/mapper',
|
|
37
45
|
menu: typedMenu,
|
|
38
46
|
},
|
|
47
|
+
parameters: {
|
|
48
|
+
docs: {
|
|
49
|
+
description: {
|
|
50
|
+
story:
|
|
51
|
+
'Renders ReqraftMenu with the /Interfaces/mapper path — the matching menu entry is highlighted as active.',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
39
55
|
play: async () => {
|
|
40
56
|
await testsWaitForText('Interfaces & More');
|
|
41
57
|
},
|
|
@@ -47,6 +63,14 @@ export const ActiveMenuItemIntent: Story = {
|
|
|
47
63
|
menu: typedMenu,
|
|
48
64
|
activeItemIntent: 'success',
|
|
49
65
|
},
|
|
66
|
+
parameters: {
|
|
67
|
+
docs: {
|
|
68
|
+
description: {
|
|
69
|
+
story:
|
|
70
|
+
'Renders ReqraftMenu with the /Interfaces/mapper path and a "success" activeItemIntent — the active entry uses the success intent for its highlight colour.',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
50
74
|
play: async () => {
|
|
51
75
|
await testsWaitForText('Interfaces & More');
|
|
52
76
|
},
|
|
@@ -57,6 +81,14 @@ export const WithDefaultQuery: Story = {
|
|
|
57
81
|
menu: typedMenu,
|
|
58
82
|
defaultQuery: 'mapper',
|
|
59
83
|
},
|
|
84
|
+
parameters: {
|
|
85
|
+
docs: {
|
|
86
|
+
description: {
|
|
87
|
+
story:
|
|
88
|
+
'Renders ReqraftMenu with a defaultQuery of "mapper" — the search input is pre-filled and the visible items are narrowed to those matching the query.',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
60
92
|
play: async () => {
|
|
61
93
|
await testsWaitForText('Interfaces & More');
|
|
62
94
|
await expect(document.querySelector('.reqore-input')).toHaveValue('mapper');
|
|
@@ -69,6 +101,14 @@ export const Filtered: Story = {
|
|
|
69
101
|
menu: typedMenu,
|
|
70
102
|
onQueryChange: fn(),
|
|
71
103
|
},
|
|
104
|
+
parameters: {
|
|
105
|
+
docs: {
|
|
106
|
+
description: {
|
|
107
|
+
story:
|
|
108
|
+
'Renders ReqraftMenu with an onQueryChange handler. Typing "step" into the search filter narrows the visible items to just the matching entries.',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
72
112
|
play: async () => {
|
|
73
113
|
await testsWaitForText('Interfaces & More');
|
|
74
114
|
await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'step' } });
|
|
@@ -82,6 +122,12 @@ export const Filtered: Story = {
|
|
|
82
122
|
export const WidthFromStorage: Story = {
|
|
83
123
|
...ActivePath,
|
|
84
124
|
parameters: {
|
|
125
|
+
docs: {
|
|
126
|
+
description: {
|
|
127
|
+
story:
|
|
128
|
+
'Renders ReqraftMenu backed by user storage that already holds a persisted width — the menu mounts at the stored width rather than the default.',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
85
131
|
mockData: [...storiesStorageMock],
|
|
86
132
|
},
|
|
87
133
|
};
|
|
@@ -93,6 +139,14 @@ export const WithCustomChildren: Story = {
|
|
|
93
139
|
topChildren: <div style={{ padding: 10 }}>Top Custom Child</div>,
|
|
94
140
|
bottomChildren: <div style={{ padding: 10 }}>Bottom Custom Child</div>,
|
|
95
141
|
},
|
|
142
|
+
parameters: {
|
|
143
|
+
docs: {
|
|
144
|
+
description: {
|
|
145
|
+
story:
|
|
146
|
+
'Renders ReqraftMenu with topChildren and bottomChildren slots — custom nodes appear above and below the menu list.',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
96
150
|
play: async () => {
|
|
97
151
|
await testsWaitForText('Interfaces & More');
|
|
98
152
|
await testsWaitForText('Top Custom Child');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
3
2
|
import { useState } from 'react';
|
|
3
|
+
import { expect, fn, userEvent, waitFor, within } from 'storybook/test';
|
|
4
4
|
import { sleep } from '../../../__tests__/utils';
|
|
5
5
|
import { StoryMeta } from '../../types';
|
|
6
6
|
import {
|
|
@@ -83,14 +83,16 @@ function mockTokenizeQonsole(text: string): number[] {
|
|
|
83
83
|
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
84
84
|
const line = lines[lineIdx];
|
|
85
85
|
// Strings first so they swallow any `--`/digits inside their quotes.
|
|
86
|
-
const TOKEN_RE =
|
|
87
|
-
/("(?:\\.|[^"\\])*")|(\d+(?:\.\d+)?)|(--[A-Za-z][\w-]*)|(\/[A-Za-z][\w-]*)/g;
|
|
86
|
+
const TOKEN_RE = /("(?:\\.|[^"\\])*")|(\d+(?:\.\d+)?)|(--[A-Za-z][\w-]*)|(\/[A-Za-z][\w-]*)/g;
|
|
88
87
|
let match: RegExpExecArray | null;
|
|
89
88
|
while ((match = TOKEN_RE.exec(line)) !== null) {
|
|
90
89
|
let type = -1;
|
|
91
|
-
if (match[1])
|
|
92
|
-
|
|
93
|
-
else if (match[
|
|
90
|
+
if (match[1])
|
|
91
|
+
type = 11; // string
|
|
92
|
+
else if (match[2])
|
|
93
|
+
type = 12; // number
|
|
94
|
+
else if (match[3])
|
|
95
|
+
type = 9; // modifier — `--flag`
|
|
94
96
|
else if (match[4]) type = 8; // keyword — `/verb`
|
|
95
97
|
if (type >= 0) {
|
|
96
98
|
tokens.push({
|
|
@@ -102,7 +104,7 @@ function mockTokenizeQonsole(text: string): number[] {
|
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
|
-
tokens.sort((a, b) =>
|
|
107
|
+
tokens.sort((a, b) => a.line - b.line || a.char - b.char);
|
|
106
108
|
// Delta-encode per LSP spec.
|
|
107
109
|
const data: number[] = [];
|
|
108
110
|
let prevLine = 0;
|
|
@@ -127,11 +129,7 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
|
|
|
127
129
|
experimental: {
|
|
128
130
|
qonsole: {
|
|
129
131
|
version: '1.0',
|
|
130
|
-
methods: [
|
|
131
|
-
'qonsole/assist',
|
|
132
|
-
'qonsole/validate',
|
|
133
|
-
'qonsole/setContext',
|
|
134
|
-
],
|
|
132
|
+
methods: ['qonsole/assist', 'qonsole/validate', 'qonsole/setContext'],
|
|
135
133
|
},
|
|
136
134
|
},
|
|
137
135
|
},
|
|
@@ -213,8 +211,7 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
|
|
|
213
211
|
short_desc: 'Guided setup for a new connection',
|
|
214
212
|
verb: 'create',
|
|
215
213
|
resource: 'connections',
|
|
216
|
-
start_path:
|
|
217
|
-
'/api/latest/qonsole/wizards/create-connection/start',
|
|
214
|
+
start_path: '/api/latest/qonsole/wizards/create-connection/start',
|
|
218
215
|
},
|
|
219
216
|
command: {
|
|
220
217
|
title: 'Start Create connection wizard',
|
|
@@ -227,8 +224,7 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
|
|
|
227
224
|
short_desc: 'Guided setup for a new connection',
|
|
228
225
|
verb: 'create',
|
|
229
226
|
resource: 'connections',
|
|
230
|
-
start_path:
|
|
231
|
-
'/api/latest/qonsole/wizards/create-connection/start',
|
|
227
|
+
start_path: '/api/latest/qonsole/wizards/create-connection/start',
|
|
232
228
|
},
|
|
233
229
|
],
|
|
234
230
|
},
|
|
@@ -237,10 +233,34 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
|
|
|
237
233
|
];
|
|
238
234
|
} else if (ctx.type === 'resource') {
|
|
239
235
|
items = [
|
|
240
|
-
withTextEdit({
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
236
|
+
withTextEdit({
|
|
237
|
+
label: 'services',
|
|
238
|
+
insertText: 'services',
|
|
239
|
+
kind: 7,
|
|
240
|
+
detail: 'Service interfaces',
|
|
241
|
+
commitCharacters: [' '],
|
|
242
|
+
}),
|
|
243
|
+
withTextEdit({
|
|
244
|
+
label: 'workflows',
|
|
245
|
+
insertText: 'workflows',
|
|
246
|
+
kind: 7,
|
|
247
|
+
detail: 'Workflow interfaces',
|
|
248
|
+
commitCharacters: [' '],
|
|
249
|
+
}),
|
|
250
|
+
withTextEdit({
|
|
251
|
+
label: 'jobs',
|
|
252
|
+
insertText: 'jobs',
|
|
253
|
+
kind: 7,
|
|
254
|
+
detail: 'Job interfaces',
|
|
255
|
+
commitCharacters: [' '],
|
|
256
|
+
}),
|
|
257
|
+
withTextEdit({
|
|
258
|
+
label: 'users',
|
|
259
|
+
insertText: 'users',
|
|
260
|
+
kind: 7,
|
|
261
|
+
detail: 'IDP users',
|
|
262
|
+
commitCharacters: [' '],
|
|
263
|
+
}),
|
|
244
264
|
];
|
|
245
265
|
} else if (ctx.type === 'flag') {
|
|
246
266
|
items = [
|
|
@@ -275,9 +295,24 @@ function setupBasicQonsoleMockServer(): IMockLspServer {
|
|
|
275
295
|
];
|
|
276
296
|
} else if (ctx.type === 'flag-value') {
|
|
277
297
|
items = [
|
|
278
|
-
withTextEdit({
|
|
279
|
-
|
|
280
|
-
|
|
298
|
+
withTextEdit({
|
|
299
|
+
label: 'qorus',
|
|
300
|
+
insertText: 'qorus',
|
|
301
|
+
kind: 12,
|
|
302
|
+
detail: 'Qorus core app',
|
|
303
|
+
}),
|
|
304
|
+
withTextEdit({
|
|
305
|
+
label: 'qorus-ide',
|
|
306
|
+
insertText: 'qorus-ide',
|
|
307
|
+
kind: 12,
|
|
308
|
+
detail: 'Qorus IDE',
|
|
309
|
+
}),
|
|
310
|
+
withTextEdit({
|
|
311
|
+
label: 'qorus-creator',
|
|
312
|
+
insertText: 'qorus-creator',
|
|
313
|
+
kind: 12,
|
|
314
|
+
detail: 'Qorus Creator',
|
|
315
|
+
}),
|
|
281
316
|
];
|
|
282
317
|
}
|
|
283
318
|
}
|
|
@@ -363,6 +398,14 @@ export const BasicMock: Story = {
|
|
|
363
398
|
args: {
|
|
364
399
|
initialValue: '/list services ',
|
|
365
400
|
},
|
|
401
|
+
parameters: {
|
|
402
|
+
docs: {
|
|
403
|
+
description: {
|
|
404
|
+
story:
|
|
405
|
+
'Renders the QonsoleSmartInput against a mocked Qonsole LSP. Typing "-" after "/list services " opens the flag dropdown with --desc, --limit and --search; pressing Enter accepts the first item without producing a stray double dash.',
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
},
|
|
366
409
|
async beforeEach() {
|
|
367
410
|
const server = setupBasicQonsoleMockServer();
|
|
368
411
|
return () => server.close();
|
|
@@ -474,6 +517,12 @@ export const LiveQonsoleWithContext: Story = {
|
|
|
474
517
|
},
|
|
475
518
|
parameters: {
|
|
476
519
|
chromatic: { disable: true },
|
|
520
|
+
docs: {
|
|
521
|
+
description: {
|
|
522
|
+
story:
|
|
523
|
+
'Renders the QonsoleSmartInput against the real Qonsole LSP with a pre-bound "/use services" context — predictive text is scoped to the services resource via the qonsole/setContext call.',
|
|
524
|
+
},
|
|
525
|
+
},
|
|
477
526
|
},
|
|
478
527
|
};
|
|
479
528
|
|
|
@@ -520,8 +569,7 @@ export const WithDiagnostics: Story = {
|
|
|
520
569
|
start: { line: 0, character: 6 },
|
|
521
570
|
end: { line: 0, character: 14 },
|
|
522
571
|
},
|
|
523
|
-
message:
|
|
524
|
-
'Unknown resource "services" — did you mean "service"?',
|
|
572
|
+
message: 'Unknown resource "services" — did you mean "service"?',
|
|
525
573
|
severity: 1,
|
|
526
574
|
},
|
|
527
575
|
{
|
|
@@ -676,9 +724,9 @@ export const WithWizardItems: Story = {
|
|
|
676
724
|
() => {
|
|
677
725
|
const dropdown = document.querySelector('.reqore-menu');
|
|
678
726
|
expect(dropdown).not.toBeNull();
|
|
679
|
-
const item = Array.from(
|
|
680
|
-
|
|
681
|
-
)
|
|
727
|
+
const item = Array.from(document.querySelectorAll('.reqore-menu-item')).find((el) =>
|
|
728
|
+
el.textContent?.includes('Create connection')
|
|
729
|
+
);
|
|
682
730
|
expect(item).toBeDefined();
|
|
683
731
|
},
|
|
684
732
|
{ timeout: 30000 }
|
|
@@ -701,7 +749,6 @@ export const WithWizardItems: Story = {
|
|
|
701
749
|
*/
|
|
702
750
|
export const SelectingWizardItemFiresCallback: Story = {
|
|
703
751
|
args: {
|
|
704
|
-
initialValue: '/',
|
|
705
752
|
onWizardStart: fn(),
|
|
706
753
|
},
|
|
707
754
|
parameters: {
|
|
@@ -727,23 +774,22 @@ export const SelectingWizardItemFiresCallback: Story = {
|
|
|
727
774
|
await userEvent.type(editable, '/');
|
|
728
775
|
await waitFor(
|
|
729
776
|
() => {
|
|
730
|
-
const item = Array.from(
|
|
731
|
-
|
|
732
|
-
)
|
|
777
|
+
const item = Array.from(document.querySelectorAll('.reqore-menu-item')).find((el) =>
|
|
778
|
+
el.textContent?.includes('Create connection')
|
|
779
|
+
);
|
|
733
780
|
expect(item).toBeDefined();
|
|
734
781
|
},
|
|
735
782
|
{ timeout: 30000 }
|
|
736
783
|
);
|
|
737
|
-
const wizardItem = Array.from(
|
|
738
|
-
|
|
739
|
-
)
|
|
784
|
+
const wizardItem = Array.from(document.querySelectorAll('.reqore-menu-item')).find((el) =>
|
|
785
|
+
el.textContent?.includes('Create connection')
|
|
786
|
+
);
|
|
740
787
|
await userEvent.click(wizardItem as HTMLElement);
|
|
741
788
|
// The callback fired with the wizard descriptor...
|
|
742
789
|
await waitFor(() => expect(args.onWizardStart).toHaveBeenCalled(), {
|
|
743
790
|
timeout: 30000,
|
|
744
791
|
});
|
|
745
|
-
const callArg = (args.onWizardStart as ReturnType<typeof fn>).mock
|
|
746
|
-
.calls[0][0];
|
|
792
|
+
const callArg = (args.onWizardStart as ReturnType<typeof fn>).mock.calls[0][0];
|
|
747
793
|
expect(callArg).toMatchObject({ action: 'start-wizard' });
|
|
748
794
|
// ...and NO text was inserted (wizard launch ≠ completion insert).
|
|
749
795
|
expect(editable.textContent).toBe('/');
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { ReqoreControlGroup, ReqoreDropdown, ReqoreInput } from '@qoretechnologies/reqore';
|
|
2
|
+
import { IReqoreIconName } from '@qoretechnologies/reqore/dist/types/icons';
|
|
3
|
+
import styled, { css } from 'styled-components';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* A search box with filter dropdowns beside it — the strip that sits above a list
|
|
7
|
+
* and narrows it.
|
|
8
|
+
*
|
|
9
|
+
* This existed twice, built independently either side of the package boundary: the
|
|
10
|
+
* ticket list in qorus-ide and the References tab here in reqraft. Same
|
|
11
|
+
* composition (ReqoreControlGroup > ReqoreInput + N ReqoreDropdowns), but they had
|
|
12
|
+
* drifted — one sized the group, the other sized each control; one wrapped, the
|
|
13
|
+
* other stacked; one moved the dropdown caret to the right slot and suppressed the
|
|
14
|
+
* default, the other did not. Two strips that should be indistinguishable ended up
|
|
15
|
+
* visibly different, and any fix had to be made twice.
|
|
16
|
+
*
|
|
17
|
+
* It lives in reqraft rather than Reqore because it is a composition of Reqore
|
|
18
|
+
* primitives, not a new primitive — there is nothing here Reqore is missing.
|
|
19
|
+
*
|
|
20
|
+
* The frosted container is part of the component, not the caller's job. Extracting
|
|
21
|
+
* only the controls left the two surfaces still looking different: one bar sat in a
|
|
22
|
+
* bordered, blurred card, the other bare on the page. If the bar is meant to read
|
|
23
|
+
* the same everywhere then its own chrome has to travel with it.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const Container = styled.div<{ $sticky?: boolean }>`
|
|
27
|
+
padding: 8px;
|
|
28
|
+
border-radius: 8px;
|
|
29
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
30
|
+
background: rgba(255, 255, 255, 0.04);
|
|
31
|
+
backdrop-filter: blur(6px);
|
|
32
|
+
|
|
33
|
+
${({ $sticky }) =>
|
|
34
|
+
$sticky &&
|
|
35
|
+
css`
|
|
36
|
+
position: sticky;
|
|
37
|
+
top: 0;
|
|
38
|
+
z-index: 2;
|
|
39
|
+
`}
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
export interface ISearchFilterOption {
|
|
43
|
+
label: string;
|
|
44
|
+
icon?: IReqoreIconName;
|
|
45
|
+
selected?: boolean;
|
|
46
|
+
onClick: () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ISearchFilter {
|
|
50
|
+
/** Icon for the trigger — usually the selected option's. */
|
|
51
|
+
icon?: IReqoreIconName;
|
|
52
|
+
/** Trigger label — usually the selected option's. */
|
|
53
|
+
label: string;
|
|
54
|
+
items: ISearchFilterOption[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ISearchFilterBarProps {
|
|
58
|
+
value: string;
|
|
59
|
+
onChange: (value: string) => void;
|
|
60
|
+
placeholder: string;
|
|
61
|
+
filters?: ISearchFilter[];
|
|
62
|
+
/**
|
|
63
|
+
* Stack the search over its filters. On a phone the box and two dropdowns
|
|
64
|
+
* cannot share a row without the input collapsing to a few characters, so the
|
|
65
|
+
* caller passes its own breakpoint result rather than this guessing.
|
|
66
|
+
*/
|
|
67
|
+
stacked?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Pin the bar to the top of its scroll container. For a bar inside a scrolling
|
|
70
|
+
* panel (the References tab); a bar above a page-level list does not need it.
|
|
71
|
+
*/
|
|
72
|
+
sticky?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const SearchFilterBar = ({
|
|
76
|
+
value,
|
|
77
|
+
onChange,
|
|
78
|
+
placeholder,
|
|
79
|
+
filters = [],
|
|
80
|
+
stacked,
|
|
81
|
+
sticky,
|
|
82
|
+
}: ISearchFilterBarProps) => (
|
|
83
|
+
<Container $sticky={sticky}>
|
|
84
|
+
{/* `size='small'` on the GROUP, not per control: the bar filters the content, it
|
|
85
|
+
is not content — at the default size the dropdowns read as primary actions and
|
|
86
|
+
crowd the search field. */}
|
|
87
|
+
<ReqoreControlGroup
|
|
88
|
+
fluid
|
|
89
|
+
wrap
|
|
90
|
+
gapSize='small'
|
|
91
|
+
size='small'
|
|
92
|
+
vertical={stacked}
|
|
93
|
+
verticalAlign={stacked ? undefined : 'center'}
|
|
94
|
+
>
|
|
95
|
+
<ReqoreInput
|
|
96
|
+
icon='Search2Line'
|
|
97
|
+
placeholder={placeholder}
|
|
98
|
+
value={value}
|
|
99
|
+
onChange={(event) => onChange((event.currentTarget as HTMLInputElement).value)}
|
|
100
|
+
onClearClick={value ? () => onChange('') : undefined}
|
|
101
|
+
minimal
|
|
102
|
+
fluid
|
|
103
|
+
/>
|
|
104
|
+
{filters.length ?
|
|
105
|
+
<ReqoreControlGroup gapSize='small' size='small' fluid={stacked}>
|
|
106
|
+
{filters.map((filter, index) => (
|
|
107
|
+
<ReqoreDropdown
|
|
108
|
+
key={index}
|
|
109
|
+
icon={filter.icon}
|
|
110
|
+
label={filter.label}
|
|
111
|
+
minimal
|
|
112
|
+
flat
|
|
113
|
+
/* stretch to share the row when stacked; hug the label otherwise */
|
|
114
|
+
fixed={!stacked}
|
|
115
|
+
/* the caret defaults to the LEFT slot, where the filter's own icon
|
|
116
|
+
already sits — without this each trigger shows two glyphs */
|
|
117
|
+
rightIcon='ArrowDownSLine'
|
|
118
|
+
showCaret={false}
|
|
119
|
+
items={filter.items.map((item) => ({
|
|
120
|
+
label: item.label,
|
|
121
|
+
icon: item.icon,
|
|
122
|
+
selected: item.selected,
|
|
123
|
+
onClick: item.onClick,
|
|
124
|
+
}))}
|
|
125
|
+
/>
|
|
126
|
+
))}
|
|
127
|
+
</ReqoreControlGroup>
|
|
128
|
+
: null}
|
|
129
|
+
</ReqoreControlGroup>
|
|
130
|
+
</Container>
|
|
131
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SearchFilterBar';
|
|
@@ -72,6 +72,14 @@ export const BasicMock: Story = {
|
|
|
72
72
|
initialValue: '',
|
|
73
73
|
triggerCharacters: new Set([' ', '.']),
|
|
74
74
|
},
|
|
75
|
+
parameters: {
|
|
76
|
+
docs: {
|
|
77
|
+
description: {
|
|
78
|
+
story:
|
|
79
|
+
'Renders the SmartEditor against a mocked LSP server. Typing the "." trigger character sends a completion request and the dropdown mounts with the apple and banana suggestions.',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
75
83
|
async beforeEach() {
|
|
76
84
|
const lsp = createMockLspServer(MOCK_LSP_URL, {
|
|
77
85
|
capabilities: {},
|
|
@@ -295,6 +303,14 @@ export const WithDiagnostics: Story = {
|
|
|
295
303
|
languageId: 'demo',
|
|
296
304
|
initialValue: 'list services in pricing',
|
|
297
305
|
},
|
|
306
|
+
parameters: {
|
|
307
|
+
docs: {
|
|
308
|
+
description: {
|
|
309
|
+
story:
|
|
310
|
+
'Renders the SmartEditor against a mocked LSP server that pushes diagnostics on didOpen — the text is decorated with wavy underlines and the stacked message panel below the editor lists both diagnostics.',
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
},
|
|
298
314
|
async beforeEach() {
|
|
299
315
|
const lsp = createMockLspServer(MOCK_LSP_URL, {
|
|
300
316
|
capabilities: {},
|
|
@@ -342,6 +358,14 @@ export const ReadOnly: Story = {
|
|
|
342
358
|
initialValue: 'frozen content',
|
|
343
359
|
readOnly: true,
|
|
344
360
|
},
|
|
361
|
+
parameters: {
|
|
362
|
+
docs: {
|
|
363
|
+
description: {
|
|
364
|
+
story:
|
|
365
|
+
'Renders the SmartEditor in read-only mode over an initial value — the Slate editor mounts as contenteditable=false so the content shows but cannot be edited.',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
345
369
|
async beforeEach() {
|
|
346
370
|
const lsp = createMockLspServer(MOCK_LSP_URL, { capabilities: {} });
|
|
347
371
|
return () => lsp.close();
|
|
@@ -367,6 +391,14 @@ export const LoadingOverlay: Story = {
|
|
|
367
391
|
languageId: 'demo',
|
|
368
392
|
initialValue: 'visible under the overlay',
|
|
369
393
|
},
|
|
394
|
+
parameters: {
|
|
395
|
+
docs: {
|
|
396
|
+
description: {
|
|
397
|
+
story:
|
|
398
|
+
'Renders the SmartEditor against a mock LSP that never finishes initialize — the "Connecting to language server" overlay stays over the editor for the whole session.',
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
},
|
|
370
402
|
async beforeEach() {
|
|
371
403
|
const lsp = createMockLspServer(MOCK_LSP_URL, {
|
|
372
404
|
capabilities: {},
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
|
4
|
+
import { StoryMeta } from '../../types';
|
|
5
|
+
import { AttachmentChips } from './AttachmentChips';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Staged-file chips for a ticket composer. Image files show a thumbnail and open the shared
|
|
9
|
+
* `ImageLightbox` on click; other files show a paperclip. The pencil renames a file inline
|
|
10
|
+
* (a File name is read-only, so the host swaps in a renamed copy); the ✕ removes it.
|
|
11
|
+
*/
|
|
12
|
+
const meta = {
|
|
13
|
+
component: AttachmentChips,
|
|
14
|
+
title: 'Components/AttachmentChips',
|
|
15
|
+
} as StoryMeta<typeof AttachmentChips>;
|
|
16
|
+
export default meta;
|
|
17
|
+
type Story = StoryObj<typeof meta>;
|
|
18
|
+
|
|
19
|
+
const imageFile = (label: string, hue: number, name: string): File => {
|
|
20
|
+
const svg =
|
|
21
|
+
`<svg xmlns='http://www.w3.org/2000/svg' width='640' height='400'>` +
|
|
22
|
+
`<rect width='640' height='400' fill='hsl(${hue},40%,16%)'/>` +
|
|
23
|
+
`<text x='24' y='210' fill='white' font-family='sans-serif' font-size='30'>${label}</text></svg>`;
|
|
24
|
+
return new File([svg], name, { type: 'image/svg+xml' });
|
|
25
|
+
};
|
|
26
|
+
const textFile = (name: string): File => new File(['log contents'], name, { type: 'text/plain' });
|
|
27
|
+
|
|
28
|
+
const FILES = [
|
|
29
|
+
imageFile('before', 265, 'screenshot-2026-07-26-17-41-44.png'),
|
|
30
|
+
imageFile('after', 205, 'canvas-after.png'),
|
|
31
|
+
textFile('server.log'),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
// Plays the composer's role: owns the file set, swaps in a renamed copy on rename.
|
|
35
|
+
const Harness = () => {
|
|
36
|
+
const [files, setFiles] = useState<File[]>(FILES);
|
|
37
|
+
return (
|
|
38
|
+
<AttachmentChips
|
|
39
|
+
files={files}
|
|
40
|
+
onRemove={(index) => setFiles((current) => current.filter((_, j) => j !== index))}
|
|
41
|
+
onRename={(index, name) =>
|
|
42
|
+
setFiles((current) =>
|
|
43
|
+
current.map((file, i) => (i === index ? new File([file], name, { type: file.type }) : file))
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Two screenshots + a log. Clicking a screenshot chip opens the lightbox (paging across the two
|
|
52
|
+
* images); the log stays a plain chip. Proves the preview opens and paints the image.
|
|
53
|
+
*/
|
|
54
|
+
export const WithImages: Story = {
|
|
55
|
+
parameters: {
|
|
56
|
+
docs: {
|
|
57
|
+
description: {
|
|
58
|
+
story:
|
|
59
|
+
'Renders staged chips for two screenshots and a log, where clicking a screenshot chip opens the shared lightbox while the log stays a plain chip.',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
render: () => <Harness />,
|
|
64
|
+
play: async ({ canvasElement }) => {
|
|
65
|
+
const canvas = within(canvasElement);
|
|
66
|
+
await expect(canvas.getByText('screenshot-2026-07-26-17-41-44.png')).toBeInTheDocument();
|
|
67
|
+
await expect(canvas.getByText('server.log')).toBeInTheDocument();
|
|
68
|
+
|
|
69
|
+
// clicking an image chip opens the shared lightbox (portals to the body)
|
|
70
|
+
await userEvent.click(canvas.getByText('canvas-after.png'));
|
|
71
|
+
await waitFor(() => expect(document.querySelector('[role="dialog"]')).not.toBeNull());
|
|
72
|
+
await waitFor(() =>
|
|
73
|
+
expect(document.body.querySelector('img[alt="canvas-after.png"]')).not.toBeNull()
|
|
74
|
+
);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Renaming from INSIDE the preview: open a chip's lightbox, use its pencil, and the new
|
|
80
|
+
* name lands on the chip too — the lightbox funnels into the same host `onRename` as the
|
|
81
|
+
* chip pencil, so both affordances stay one mechanism.
|
|
82
|
+
*/
|
|
83
|
+
export const RenameFromPreview: Story = {
|
|
84
|
+
parameters: {
|
|
85
|
+
docs: {
|
|
86
|
+
description: {
|
|
87
|
+
story:
|
|
88
|
+
'Renders the staged chips and renames a screenshot from inside its lightbox — the preview’s pencil commits the new name, and the chip outside carries it too.',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
render: () => <Harness />,
|
|
93
|
+
play: async ({ canvasElement }) => {
|
|
94
|
+
const canvas = within(canvasElement);
|
|
95
|
+
await userEvent.click(canvas.getByText('canvas-after.png'));
|
|
96
|
+
await waitFor(() => expect(document.querySelector('[role="dialog"]')).not.toBeNull());
|
|
97
|
+
|
|
98
|
+
const pencil = await waitFor(() => {
|
|
99
|
+
const el = document.body.querySelector<HTMLButtonElement>('.imagelightbox-rename');
|
|
100
|
+
if (!el) {
|
|
101
|
+
throw new Error('lightbox rename action not rendered');
|
|
102
|
+
}
|
|
103
|
+
return el;
|
|
104
|
+
});
|
|
105
|
+
await userEvent.click(pencil);
|
|
106
|
+
const input = await waitFor(() => {
|
|
107
|
+
const el = document.body.querySelector<HTMLInputElement>('.reqore-modal input');
|
|
108
|
+
if (!el) {
|
|
109
|
+
throw new Error('lightbox rename input not shown');
|
|
110
|
+
}
|
|
111
|
+
return el;
|
|
112
|
+
});
|
|
113
|
+
await userEvent.clear(input);
|
|
114
|
+
await userEvent.type(input, 'after-fix.png');
|
|
115
|
+
await userEvent.keyboard('{Enter}');
|
|
116
|
+
|
|
117
|
+
// commit ends the edit; a second Esc now closes the modal itself
|
|
118
|
+
await userEvent.keyboard('{Escape}');
|
|
119
|
+
await waitFor(() => expect(document.querySelector('[role="dialog"]')).toBeNull());
|
|
120
|
+
|
|
121
|
+
// the rename reached the host: the CHIP carries the new name
|
|
122
|
+
await waitFor(() => expect(canvas.getByText('after-fix.png')).toBeInTheDocument());
|
|
123
|
+
await expect(canvas.queryByText('canvas-after.png')).not.toBeInTheDocument();
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Renaming an auto-named screenshot: the pencil turns the chip into an inline input; Enter
|
|
129
|
+
* commits and the chip shows the new name. Discriminating — remove the rename wiring and the
|
|
130
|
+
* chip keeps its `screenshot-<timestamp>.png` name.
|
|
131
|
+
*/
|
|
132
|
+
export const Rename: Story = {
|
|
133
|
+
parameters: {
|
|
134
|
+
docs: {
|
|
135
|
+
description: {
|
|
136
|
+
story:
|
|
137
|
+
'Renders the staged chips and renames an auto-named screenshot inline — the pencil opens an input, Enter commits, and the chip shows the new name.',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
render: () => <Harness />,
|
|
142
|
+
play: async ({ canvasElement }) => {
|
|
143
|
+
const canvas = within(canvasElement);
|
|
144
|
+
const chip = canvas.getByText('screenshot-2026-07-26-17-41-44.png').closest('.reqore-tag');
|
|
145
|
+
if (!chip) {
|
|
146
|
+
throw new Error('chip not found');
|
|
147
|
+
}
|
|
148
|
+
// click the rename pencil (its own action; the ✕ is a separate reqore-tag-remove)
|
|
149
|
+
const renameAction = chip.querySelector<HTMLElement>('.reqore-tag-action:not(.reqore-tag-remove)');
|
|
150
|
+
if (!renameAction) {
|
|
151
|
+
throw new Error('rename action not rendered');
|
|
152
|
+
}
|
|
153
|
+
await userEvent.click(renameAction);
|
|
154
|
+
|
|
155
|
+
// the inline input appears, pre-filled with the current name
|
|
156
|
+
const input = await waitFor(() => {
|
|
157
|
+
const el = canvasElement.querySelector<HTMLInputElement>('input');
|
|
158
|
+
if (!el) {
|
|
159
|
+
throw new Error('rename input not shown');
|
|
160
|
+
}
|
|
161
|
+
return el;
|
|
162
|
+
});
|
|
163
|
+
await userEvent.clear(input);
|
|
164
|
+
await userEvent.type(input, 'mapper-error.png');
|
|
165
|
+
await userEvent.keyboard('{Enter}');
|
|
166
|
+
|
|
167
|
+
// the chip now carries the new name; the auto-name is gone
|
|
168
|
+
await waitFor(() => expect(canvas.getByText('mapper-error.png')).toBeInTheDocument());
|
|
169
|
+
await expect(canvas.queryByText('screenshot-2026-07-26-17-41-44.png')).not.toBeInTheDocument();
|
|
170
|
+
},
|
|
171
|
+
};
|