Skip to content

Commit 72f4dd8

Browse files
authored
Merge pull request #16 from qoretechnologies/feature/log-component
First version of log component
2 parents f3ca116 + a54ec78 commit 72f4dd8

File tree

9 files changed

+1946
-46
lines changed

9 files changed

+1946
-46
lines changed

.storybook/preview.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import withMockdate from '@netsells/storybook-mockdate';
12
import { ReqoreContent, ReqoreLayoutContent, ReqoreUIProvider } from '@qoretechnologies/reqore';
23
import { initializeReqraft } from '../src';
34

@@ -39,6 +40,7 @@ export const argTypes = {
3940
};
4041

4142
export const decorators = [
43+
withMockdate,
4244
(Story, context) => {
4345
const Reqraft = initializeReqraft({
4446
instance: 'https://hq.qoretechnologies.com:8092/',

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@qoretechnologies/reqraft",
3-
"version": "0.4.0",
3+
"version": "0.5.0",
44
"description": "ReQraft is a collection of React components and hooks that are used across Qore Technologies' products made using the ReQore component library from Qore.",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
@@ -52,7 +52,8 @@
5252
"@babel/preset-react": "^7.12.10",
5353
"@babel/preset-typescript": "^7.12.7",
5454
"@chromatic-com/storybook": "^1.5.0",
55-
"@qoretechnologies/reqore": "^0.43.0",
55+
"@netsells/storybook-mockdate": "^0.3.3",
56+
"@qoretechnologies/reqore": "^0.46.6",
5657
"@storybook/addon-actions": "^8.1.7",
5758
"@storybook/addon-essentials": "^8.1.7",
5859
"@storybook/addon-interactions": "^8.1.7",
@@ -74,7 +75,6 @@
7475
"@types/react-color": "^3.0.12",
7576
"@types/react-dom": "^18.3.0",
7677
"@types/react-query": "^1.2.9",
77-
"@types/styled-components": "^5.1.34",
7878
"@typescript-eslint/eslint-plugin": "^7.8.0",
7979
"@typescript-eslint/parser": "^7.8.0",
8080
"babel-jest": "^29.7.0",

src/components/log/Log.stories.tsx

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import { StoryObj } from '@storybook/react';
2+
import { expect, fireEvent, fn } from '@storybook/test';
3+
import { Server } from 'mock-socket';
4+
import { sleep, testsClickButton, testsWaitForText } from '../../../__tests__/utils';
5+
import { StoryMeta } from '../../types';
6+
import { ReqraftLog } from './Log';
7+
8+
const meta = {
9+
component: ReqraftLog,
10+
title: 'Components/Log',
11+
args: {
12+
url: 'log-test',
13+
label: 'Testing Log',
14+
socketOptions: {
15+
openOnMount: true,
16+
closeOnUnmount: true,
17+
},
18+
onConnect: fn(),
19+
onMessage: fn(),
20+
onDisconnect: fn(),
21+
onConnectionError: fn(),
22+
},
23+
parameters: {
24+
mockdate: new Date('2024-01-01T08:00:00.000Z'),
25+
},
26+
async beforeEach() {
27+
const url = `wss://hq.qoretechnologies.com:8092/log-test?token=${process.env.REACT_APP_QORUS_TOKEN}`;
28+
let server = new Server(url);
29+
let killTimeout: NodeJS.Timeout;
30+
let fakeInterval: NodeJS.Timeout;
31+
const fakeMessages = [
32+
'This is a message',
33+
'WARNING: This is a warning message',
34+
'This is another message',
35+
'This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature, which should be able to handle this message without any issues. This is a very long message that should be split into multiple lines to test the word wrap feature,',
36+
'ERROR: This is an error message',
37+
'This is a new message',
38+
'THIS IS A MESSAGE IN ALL CAPS',
39+
'This is a message with a link: https://www.qoretechnologies.com',
40+
];
41+
let fakeMessageIndex = 0;
42+
43+
server.on('connection', (socket) => {
44+
if (killTimeout) {
45+
server.close();
46+
return;
47+
}
48+
49+
fakeInterval = setInterval(() => {
50+
socket.send(fakeMessages[fakeMessageIndex]);
51+
fakeMessageIndex = (fakeMessageIndex + 1) % fakeMessages.length;
52+
53+
if (fakeMessageIndex === 0) {
54+
clearInterval(fakeInterval);
55+
fakeInterval = null;
56+
}
57+
}, 100);
58+
59+
socket.on('message', (data) => {
60+
if (data === 'ping') {
61+
socket.send('pong');
62+
return;
63+
}
64+
65+
if (data === 'kill') {
66+
server.close();
67+
68+
killTimeout = setTimeout(() => {
69+
server = new Server(url);
70+
killTimeout = null;
71+
}, 3000);
72+
73+
return;
74+
}
75+
76+
socket.send(`Received message: ${data}`);
77+
});
78+
});
79+
80+
return () => {
81+
killTimeout && clearTimeout(killTimeout);
82+
killTimeout = null;
83+
fakeInterval && clearInterval(fakeInterval);
84+
fakeInterval = null;
85+
fakeMessageIndex = 0;
86+
87+
server.close();
88+
};
89+
},
90+
} as StoryMeta<typeof ReqraftLog>;
91+
92+
export default meta;
93+
export type Story = StoryObj<typeof meta>;
94+
95+
export const Basic: Story = {
96+
play: async () => {
97+
await testsWaitForText('This is a message with a link: https://www.qoretechnologies.com');
98+
},
99+
};
100+
101+
export const Filterable: Story = {
102+
...Basic,
103+
args: {
104+
filterable: true,
105+
},
106+
play: async (args) => {
107+
await Basic.play(args);
108+
await fireEvent.change(document.querySelector('.reqore-input'), { target: { value: 'error' } });
109+
await expect(document.querySelectorAll('.reqraft-log-message')).toHaveLength(1);
110+
},
111+
};
112+
113+
export const WithTimestamps: Story = {
114+
...Basic,
115+
args: {
116+
showTimestamps: true,
117+
},
118+
};
119+
120+
export const WithDefaultMessages: Story = {
121+
...Basic,
122+
args: {
123+
showTimestamps: true,
124+
defaultMessages: [
125+
{ message: 'This is a default message', timestamp: '08:00:00.000' },
126+
{ message: 'This is another default message', timestamp: '08:00:00.000' },
127+
],
128+
},
129+
};
130+
131+
export const WithCopyableMessages: Story = {
132+
...Basic,
133+
args: {
134+
allowMessageCopy: true,
135+
},
136+
play: async (args) => {
137+
await Basic.play(args);
138+
},
139+
};
140+
141+
export const WithDeletableMessages: Story = {
142+
...Basic,
143+
args: {
144+
allowMessageCopy: true,
145+
allowMessageDeletion: true,
146+
},
147+
play: async (args) => {
148+
await Basic.play(args);
149+
await testsClickButton({ selector: '.reqraft-log-delete-message', nth: 4 });
150+
},
151+
};
152+
153+
export const Pause: Story = {
154+
...Basic,
155+
args: {
156+
allowMessageDeletion: true,
157+
},
158+
play: async () => {
159+
await testsWaitForText('This is another message');
160+
await testsClickButton({ selector: '.reqraft-log-pause-resume' });
161+
},
162+
};
163+
164+
export const Clear: Story = {
165+
...Basic,
166+
args: {
167+
allowMessageDeletion: true,
168+
},
169+
play: async (args) => {
170+
await Basic.play(args);
171+
await testsClickButton({ selector: '.reqraft-log-clear' });
172+
await testsWaitForText('No messages');
173+
},
174+
};
175+
176+
export const FormattedMessages: Story = {
177+
...Basic,
178+
args: {
179+
messageFormatter: ({ message, ...rest }) => {
180+
if (message.includes('WARNING')) {
181+
return { message, intent: 'warning' };
182+
}
183+
184+
if (message.includes('ERROR')) {
185+
return { message, intent: 'danger' };
186+
}
187+
188+
if (message.includes('ALL CAPS')) {
189+
return { message, intent: 'info' };
190+
}
191+
192+
return { message, ...rest };
193+
},
194+
},
195+
play: async (args) => {
196+
await Basic.play(args);
197+
},
198+
};
199+
200+
export const CanSendMessages: Story = {
201+
...Basic,
202+
args: {
203+
canSendMessages: true,
204+
showTimestamps: true,
205+
},
206+
play: async (args) => {
207+
await Basic.play(args);
208+
},
209+
};
210+
211+
export const WithAutoScroll: Story = {
212+
...Basic,
213+
args: {
214+
autoScroll: true,
215+
style: { height: '200px' },
216+
},
217+
play: async (args) => {
218+
await Basic.play(args);
219+
},
220+
};
221+
222+
export const WithAutoScrollAndManualScroll: Story = {
223+
...WithAutoScroll,
224+
args: {
225+
autoScroll: true,
226+
style: { height: '200px' },
227+
},
228+
play: async (args) => {
229+
await WithAutoScroll.play(args);
230+
await fireEvent.scroll(document.querySelector('.reqore-panel-content'), {
231+
target: { scrollTop: 100 },
232+
});
233+
await sleep(100);
234+
},
235+
};

0 commit comments

Comments
 (0)