Message truncation (#457)

* Implement message truncation
This commit is contained in:
Alexander Krivonosov 2021-05-18 10:05:25 +03:00 committed by GitHub
parent 1b5e9b4bca
commit 7801c292af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 214 additions and 24 deletions

View file

@ -22,6 +22,7 @@
"react-datepicker": "^3.7.0", "react-datepicker": "^3.7.0",
"react-dom": "^17.0.1", "react-dom": "^17.0.1",
"react-hook-form": "^6.15.5", "react-hook-form": "^6.15.5",
"react-json-tree": "^0.15.0",
"react-multi-select-component": "^4.0.0", "react-multi-select-component": "^4.0.0",
"react-redux": "^7.2.2", "react-redux": "^7.2.2",
"react-router": "^5.2.0", "react-router": "^5.2.0",

View file

@ -0,0 +1,48 @@
import React from 'react';
import JSONTree from 'react-json-tree';
import theme from './theme';
interface MessageContentProps {
message: string;
}
const MessageContent: React.FC<MessageContentProps> = ({ message }) => {
const [isFolded, setIsFolded] = React.useState(message.length > 250);
let fullMessage: Record<string, string> | string;
const cutMessage = `${message.slice(0, 250)}...`;
try {
fullMessage = JSON.parse(message);
} catch (e) {
fullMessage = message;
}
return (
<div className="is-flex is-flex-direction-column is-align-items-center">
{isFolded ? (
<p className="has-content-overflow-ellipsis">{cutMessage}</p>
) : (
<JSONTree
data={fullMessage}
theme={theme}
shouldExpandNode={() => true}
hideRoot
/>
)}
{isFolded && (
<button
type="button"
className="button is-small mt-2"
onClick={() => setIsFolded((state) => !state)}
title="Expand to JSON"
>
<span className="icon is-small">
<i className="fas fa-chevron-down" />
</span>
</button>
)}
</div>
);
};
export default MessageContent;

View file

@ -3,9 +3,10 @@ import { format } from 'date-fns';
import { TopicMessage } from 'generated-sources'; import { TopicMessage } from 'generated-sources';
import Dropdown from 'components/common/Dropdown/Dropdown'; import Dropdown from 'components/common/Dropdown/Dropdown';
import DropdownItem from 'components/common/Dropdown/DropdownItem'; import DropdownItem from 'components/common/Dropdown/DropdownItem';
import JSONEditor from 'components/common/JSONEditor/JSONEditor';
import useDataSaver from 'lib/hooks/useDataSaver'; import useDataSaver from 'lib/hooks/useDataSaver';
import MessageContent from './MessageContent';
export interface MessageItemProp { export interface MessageItemProp {
partition: TopicMessage['partition']; partition: TopicMessage['partition'];
offset: TopicMessage['offset']; offset: TopicMessage['offset'];
@ -29,13 +30,7 @@ const MessageItem: React.FC<MessageItemProp> = ({
<td style={{ width: 150 }}>{offset}</td> <td style={{ width: 150 }}>{offset}</td>
<td style={{ width: 100 }}>{partition}</td> <td style={{ width: 100 }}>{partition}</td>
<td style={{ wordBreak: 'break-word' }}> <td style={{ wordBreak: 'break-word' }}>
<JSONEditor <MessageContent message={JSON.stringify(content, null, '\t')} />
readOnly
value={JSON.stringify(content, null, '\t')}
name="latestSchema"
highlightActiveLine={false}
height="300px"
/>
</td> </td>
<td className="has-text-right"> <td className="has-text-right">
<Dropdown <Dropdown

View file

@ -0,0 +1,21 @@
import { shallow } from 'enzyme';
import React from 'react';
import MessageContent from 'components/Topics/Topic/Details/Messages/MessageContent';
import { messageContent } from './fixtures';
describe('MessageContent', () => {
const component = shallow(<MessageContent message={messageContent} />);
describe('when it is folded', () => {
it('matches the snapshot', () => {
expect(component).toMatchSnapshot();
});
});
describe('when it is unfolded', () => {
it('matches the snapshot', () => {
component.find('button').simulate('click');
expect(component).toMatchSnapshot();
});
});
});

View file

@ -10,12 +10,12 @@ jest.mock('date-fns', () => ({
describe('MessageItem', () => { describe('MessageItem', () => {
describe('when content is defined', () => { describe('when content is defined', () => {
it('renders table row with JSONEditor', () => { it('renders table row with MessageContent', () => {
const wrapper = shallow(<MessageItem {...messages[0]} />); const wrapper = shallow(<MessageItem {...messages[0]} />);
expect(wrapper.find('tr').length).toEqual(1); expect(wrapper.find('tr').length).toEqual(1);
expect(wrapper.find('td').length).toEqual(5); expect(wrapper.find('td').length).toEqual(5);
expect(wrapper.find('JSONEditor').length).toEqual(1); expect(wrapper.find('MessageContent').length).toEqual(1);
}); });
it('matches snapshot', () => { it('matches snapshot', () => {

View file

@ -65,8 +65,8 @@ describe('Messages', () => {
it('renders table', () => { it('renders table', () => {
expect(messagesWrapper.exists('.table.is-fullwidth')).toBeTruthy(); expect(messagesWrapper.exists('.table.is-fullwidth')).toBeTruthy();
}); });
it('renders JSONEditor', () => { it('renders MessageContent', () => {
expect(messagesWrapper.find('JSONEditor').length).toEqual(1); expect(messagesWrapper.find('MessageContent').length).toEqual(1);
}); });
it('parses message content correctly', () => { it('parses message content correctly', () => {
const messages = [ const messages = [

View file

@ -0,0 +1,90 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MessageContent when it is folded matches the snapshot 1`] = `
<div
className="is-flex is-flex-direction-column is-align-items-center"
>
<p
className="has-content-overflow-ellipsis"
>
{
"_id": "609fab8aed527f514f4e648d",
"name": "in nostrud",
"desc": "Dolore nostrud commodo magna velit ut magna voluptate sint aute. Excepteur aute culpa culpa dolor ipsum. Tempor est ut officia tempor laborum consectetur.
Amet officia eu veni...
</p>
<button
className="button is-small mt-2"
onClick={[Function]}
title="Expand to JSON"
type="button"
>
<span
className="icon is-small"
>
<i
className="fas fa-chevron-down"
/>
</span>
</button>
</div>
`;
exports[`MessageContent when it is unfolded matches the snapshot 1`] = `
<div
className="is-flex is-flex-direction-column is-align-items-center"
>
<JSONTree
collectionLimit={50}
data="{
\\"_id\\": \\"609fab8aed527f514f4e648d\\",
\\"name\\": \\"in nostrud\\",
\\"desc\\": \\"Dolore nostrud commodo magna velit ut magna voluptate sint aute. Excepteur aute culpa culpa dolor ipsum. Tempor est ut officia tempor laborum consectetur.
Amet officia eu veniam Lorem enim aliqua aute voluptate elit do sunt in magna occaecat. Nisi sit non est adipisicing adipisicing consequat duis duis tempor consequat deserunt ea quis ad. Veniam sunt culpa nostrud adipisicing cillum voluptate non est cupidatat. Eiusmod tempor officia irure et deserunt est ex laboris occaecat adipisicing occaecat in aliquip aliqua. Do laboris culpa cupidatat cillum non. Ullamco excepteur mollit voluptate anim in nisi anim elit culpa aute. Ad officia sunt proident ut ullamco officia ea fugiat culpa cillum et fugiat aliquip.
Amet non labore anim in ipsum. Et Lorem velit dolor ipsum. Irure id proident excepteur aliquip deserunt id officia dolor deserunt amet in sint. Aute in nostrud nulla ut laboris Lorem commodo nulla ipsum. Aliqua nulla commodo Lorem labore magna esse proident id ea in pariatur consectetur sint Lorem.
Cupidatat deserunt mollit tempor aliqua. Fugiat ullamco magna pariatur quis nulla magna. Esse duis labore ipsum nisi ullamco qui aute duis duis amet est laborum adipisicing magna. Est aliquip quis qui do aliquip nisi elit tempor ex aliquip. Excepteur aliquip ea deserunt amet adipisicing voluptate eiusmod sit sint exercitation exercitation. Id labore amet mollit ex commodo. Proident ex adipisicing deserunt esse Lorem tempor laborum nostrud commodo incididunt ea id.
\\",
\\"semster\\": \\"spring19\\",
\\"profile\\": \\"cs\\",
\\"degree\\": \\"bachelor\\",
\\"degreee\\": \\"master\\",
\\"degreeeee\\": \\"bachelor\\"
}"
getItemString={[Function]}
hideRoot={true}
invertTheme={true}
isCustomNode={[Function]}
keyPath={
Array [
"root",
]
}
labelRenderer={[Function]}
postprocessValue={[Function]}
shouldExpandNode={[Function]}
theme={
Object {
"author": "seth wright (http://sethawright.com)",
"base00": "#1d1f21",
"base01": "#282a2e",
"base02": "#373b41",
"base03": "#969896",
"base04": "#b4b7b4",
"base05": "#c5c8c6",
"base06": "#e0e0e0",
"base07": "#ffffff",
"base08": "#CC342B",
"base09": "#F96A38",
"base0A": "#FBA922",
"base0B": "#198844",
"base0C": "#3971ED",
"base0D": "#3971ED",
"base0E": "#A36AC7",
"base0F": "#3971ED",
"scheme": "google",
}
}
valueRenderer={[Function]}
/>
</div>
`;

View file

@ -36,12 +36,8 @@ exports[`MessageItem when content is defined matches snapshot 1`] = `
} }
} }
> >
<JSONEditor <MessageContent
height="300px" message="{
highlightActiveLine={false}
name="latestSchema"
readOnly={true}
value="{
\\"foo\\": \\"bar\\", \\"foo\\": \\"bar\\",
\\"key\\": \\"val\\" \\"key\\": \\"val\\"
}" }"
@ -113,12 +109,7 @@ exports[`MessageItem when content is undefined matches snapshot 1`] = `
} }
} }
> >
<JSONEditor <MessageContent />
height="300px"
highlightActiveLine={false}
name="latestSchema"
readOnly={true}
/>
</td> </td>
<td <td
className="has-text-right" className="has-text-right"

View file

@ -17,3 +17,14 @@ export const messages: TopicMessage[] = [
content: undefined, content: undefined,
}, },
]; ];
export const messageContent = `{
"_id": "609fab8aed527f514f4e648d",
"name": "in nostrud",
"desc": "Dolore nostrud commodo magna velit ut magna voluptate sint aute. Excepteur aute culpa culpa dolor ipsum. Tempor est ut officia tempor laborum consectetur.\r\nAmet officia eu veniam Lorem enim aliqua aute voluptate elit do sunt in magna occaecat. Nisi sit non est adipisicing adipisicing consequat duis duis tempor consequat deserunt ea quis ad. Veniam sunt culpa nostrud adipisicing cillum voluptate non est cupidatat. Eiusmod tempor officia irure et deserunt est ex laboris occaecat adipisicing occaecat in aliquip aliqua. Do laboris culpa cupidatat cillum non. Ullamco excepteur mollit voluptate anim in nisi anim elit culpa aute. Ad officia sunt proident ut ullamco officia ea fugiat culpa cillum et fugiat aliquip.\r\nAmet non labore anim in ipsum. Et Lorem velit dolor ipsum. Irure id proident excepteur aliquip deserunt id officia dolor deserunt amet in sint. Aute in nostrud nulla ut laboris Lorem commodo nulla ipsum. Aliqua nulla commodo Lorem labore magna esse proident id ea in pariatur consectetur sint Lorem.\r\nCupidatat deserunt mollit tempor aliqua. Fugiat ullamco magna pariatur quis nulla magna. Esse duis labore ipsum nisi ullamco qui aute duis duis amet est laborum adipisicing magna. Est aliquip quis qui do aliquip nisi elit tempor ex aliquip. Excepteur aliquip ea deserunt amet adipisicing voluptate eiusmod sit sint exercitation exercitation. Id labore amet mollit ex commodo. Proident ex adipisicing deserunt esse Lorem tempor laborum nostrud commodo incididunt ea id.\r\n",
"semster": "spring19",
"profile": "cs",
"degree": "bachelor",
"degreee": "master",
"degreeeee": "bachelor"
}`;

View file

@ -0,0 +1,22 @@
const theme = {
scheme: 'google',
author: 'seth wright (http://sethawright.com)',
base00: '#1d1f21',
base01: '#282a2e',
base02: '#373b41',
base03: '#969896',
base04: '#b4b7b4',
base05: '#c5c8c6',
base06: '#e0e0e0',
base07: '#ffffff',
base08: '#CC342B',
base09: '#F96A38',
base0A: '#FBA922',
base0B: '#198844',
base0C: '#3971ED',
base0D: '#3971ED',
base0E: '#A36AC7',
base0F: '#3971ED',
};
export default theme;

View file

@ -11,6 +11,17 @@
margin-right: 10px; margin-right: 10px;
} }
} }
&-content-overflow-ellipsis {
max-height: 73px;
overflow: hidden;
text-overflow: ellipsis;
background:
-webkit-linear-gradient(90deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 1) 40%);
background-clip: text;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
} }
.breadcrumb li { .breadcrumb li {