@memori.ai/memori-react 6.3.1 → 6.4.0
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/CHANGELOG.md +24 -0
- package/dist/components/Chat/Chat.js +1 -1
- package/dist/components/Chat/Chat.js.map +1 -1
- package/dist/components/ChatBubble/ChatBubble.css +16 -6
- package/dist/components/ChatBubble/ChatBubble.d.ts +2 -0
- package/dist/components/ChatBubble/ChatBubble.js +15 -7
- package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
- package/dist/components/Header/Header.css +7 -1
- package/dist/components/KnownFacts/KnownFacts.js.map +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +67 -2
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/Snippet/Snippet.d.ts +3 -1
- package/dist/components/Snippet/Snippet.js +3 -3
- package/dist/components/Snippet/Snippet.js.map +1 -1
- package/dist/components/WhyThisAnswer/WhyThisAnswer.css +73 -0
- package/dist/components/WhyThisAnswer/WhyThisAnswer.d.ts +11 -0
- package/dist/components/WhyThisAnswer/WhyThisAnswer.js +62 -0
- package/dist/components/WhyThisAnswer/WhyThisAnswer.js.map +1 -0
- package/dist/components/icons/QuestionHelp.d.ts +5 -0
- package/dist/components/icons/QuestionHelp.js +6 -0
- package/dist/components/icons/QuestionHelp.js.map +1 -0
- package/dist/components/ui/Expandable.css +15 -0
- package/dist/components/ui/Expandable.d.ts +13 -0
- package/dist/components/ui/Expandable.js +44 -0
- package/dist/components/ui/Expandable.js.map +1 -0
- package/dist/components/ui/Table.css +2 -2
- package/dist/locales/en.json +3 -0
- package/dist/locales/it.json +3 -0
- package/dist/styles.css +5 -2
- package/esm/components/Chat/Chat.js +1 -1
- package/esm/components/Chat/Chat.js.map +1 -1
- package/esm/components/ChatBubble/ChatBubble.css +16 -6
- package/esm/components/ChatBubble/ChatBubble.d.ts +2 -0
- package/esm/components/ChatBubble/ChatBubble.js +11 -3
- package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
- package/esm/components/Header/Header.css +7 -1
- package/esm/components/KnownFacts/KnownFacts.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +67 -2
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/Snippet/Snippet.d.ts +3 -1
- package/esm/components/Snippet/Snippet.js +3 -3
- package/esm/components/Snippet/Snippet.js.map +1 -1
- package/esm/components/WhyThisAnswer/WhyThisAnswer.css +73 -0
- package/esm/components/WhyThisAnswer/WhyThisAnswer.d.ts +11 -0
- package/esm/components/WhyThisAnswer/WhyThisAnswer.js +59 -0
- package/esm/components/WhyThisAnswer/WhyThisAnswer.js.map +1 -0
- package/esm/components/icons/QuestionHelp.d.ts +5 -0
- package/esm/components/icons/QuestionHelp.js +4 -0
- package/esm/components/icons/QuestionHelp.js.map +1 -0
- package/esm/components/ui/Expandable.css +15 -0
- package/esm/components/ui/Expandable.d.ts +13 -0
- package/esm/components/ui/Expandable.js +41 -0
- package/esm/components/ui/Expandable.js.map +1 -0
- package/esm/components/ui/Table.css +2 -2
- package/esm/locales/en.json +3 -0
- package/esm/locales/it.json +3 -0
- package/esm/styles.css +5 -2
- package/package.json +2 -2
- package/src/components/Chat/Chat.tsx +1 -0
- package/src/components/ChatBubble/ChatBubble.css +16 -6
- package/src/components/ChatBubble/ChatBubble.test.tsx +10 -1
- package/src/components/ChatBubble/ChatBubble.tsx +37 -7
- package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +2 -2
- package/src/components/Header/Header.css +7 -1
- package/src/components/KnownFacts/KnownFacts.tsx +0 -78
- package/src/components/MemoriWidget/MemoriWidget.tsx +71 -2
- package/src/components/Snippet/Snippet.stories.tsx +16 -0
- package/src/components/Snippet/Snippet.test.tsx +30 -0
- package/src/components/Snippet/Snippet.tsx +20 -10
- package/src/components/Snippet/__snapshots__/Snippet.test.tsx.snap +168 -0
- package/src/components/WhyThisAnswer/WhyThisAnswer.css +73 -0
- package/src/components/WhyThisAnswer/WhyThisAnswer.stories.tsx +136 -0
- package/src/components/WhyThisAnswer/WhyThisAnswer.test.tsx +124 -0
- package/src/components/WhyThisAnswer/WhyThisAnswer.tsx +151 -0
- package/src/components/WhyThisAnswer/__snapshots__/WhyThisAnswer.test.tsx.snap +13 -0
- package/src/components/icons/QuestionHelp.tsx +30 -0
- package/src/components/ui/Expandable.css +15 -0
- package/src/components/ui/Expandable.stories.tsx +53 -0
- package/src/components/ui/Expandable.test.tsx +107 -0
- package/src/components/ui/Expandable.tsx +97 -0
- package/src/components/ui/Table.css +2 -2
- package/src/components/ui/__snapshots__/Expandable.test.tsx.snap +159 -0
- package/src/locales/en.json +3 -0
- package/src/locales/it.json +3 -0
- package/src/mocks/data.ts +58 -0
- package/src/styles.css +5 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SearchMatches,
|
|
3
|
+
Message,
|
|
4
|
+
} from '@memori.ai/memori-api-client/dist/types';
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
6
|
+
import memoriApiClient from '@memori.ai/memori-api-client';
|
|
7
|
+
import Drawer from '../ui/Drawer';
|
|
8
|
+
import Spin from '../ui/Spin';
|
|
9
|
+
import Expandable from '../ui/Expandable';
|
|
10
|
+
import toast from 'react-hot-toast';
|
|
11
|
+
import { getErrori18nKey } from '../../helpers/error';
|
|
12
|
+
import { useTranslation } from 'react-i18next';
|
|
13
|
+
import Snippet from '../Snippet/Snippet';
|
|
14
|
+
|
|
15
|
+
export interface Props {
|
|
16
|
+
apiURL: string;
|
|
17
|
+
sessionID: string;
|
|
18
|
+
message: Message;
|
|
19
|
+
initialMatches?: SearchMatches[];
|
|
20
|
+
visible?: boolean;
|
|
21
|
+
closeDrawer: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const addQuestionMark = (question: string) =>
|
|
25
|
+
question.endsWith('?') ? question : `${question}?`;
|
|
26
|
+
|
|
27
|
+
const WhyThisAnswer = ({
|
|
28
|
+
message,
|
|
29
|
+
apiURL,
|
|
30
|
+
sessionID,
|
|
31
|
+
visible = true,
|
|
32
|
+
initialMatches = [],
|
|
33
|
+
closeDrawer,
|
|
34
|
+
}: Props) => {
|
|
35
|
+
const { t } = useTranslation();
|
|
36
|
+
|
|
37
|
+
const client = memoriApiClient(apiURL);
|
|
38
|
+
const searchMemory = client.search.searchMemory;
|
|
39
|
+
|
|
40
|
+
const [matches, setMatches] = useState<SearchMatches[]>(initialMatches);
|
|
41
|
+
const [loading, setLoading] = useState(false);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Fetch matching memories
|
|
45
|
+
*/
|
|
46
|
+
const fetchMemories = useCallback(async () => {
|
|
47
|
+
setLoading(true);
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const { matches, ...response } = await searchMemory(sessionID, {
|
|
51
|
+
searchType: 'Semantic',
|
|
52
|
+
numberOfResults: 5,
|
|
53
|
+
text: message.questionAnswered,
|
|
54
|
+
date: message.date,
|
|
55
|
+
placeName: message.placeName,
|
|
56
|
+
placeLatitude: message.placeLatitude,
|
|
57
|
+
placeLongitude: message.placeLongitude,
|
|
58
|
+
placeUncertaintyKm: message.placeUncertaintyKm,
|
|
59
|
+
contextVars: message.contextVars,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (response.resultCode !== 0) {
|
|
63
|
+
console.error(response);
|
|
64
|
+
toast.error(t(getErrori18nKey(response.resultCode)));
|
|
65
|
+
} else {
|
|
66
|
+
setMatches(matches ?? []);
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error('WHYTHISANSWER/FETCH', err);
|
|
70
|
+
setMatches(initialMatches ?? []);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setLoading(false);
|
|
74
|
+
}, [message, sessionID]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
fetchMemories();
|
|
77
|
+
}, [fetchMemories, message, sessionID]);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<Drawer
|
|
81
|
+
open={visible}
|
|
82
|
+
width="80%"
|
|
83
|
+
animated={false}
|
|
84
|
+
className="memori-whythisanswer-drawer"
|
|
85
|
+
onClose={() => closeDrawer()}
|
|
86
|
+
title={t('whyThisAnswer')}
|
|
87
|
+
>
|
|
88
|
+
<p>{t('whyThisAnswerHelper')}</p>
|
|
89
|
+
|
|
90
|
+
{message.questionAnswered && (
|
|
91
|
+
<p className="memori--whythisanswer-question-answered">
|
|
92
|
+
<strong>{t('question') || 'Question'}:</strong>{' '}
|
|
93
|
+
{message.questionAnswered}
|
|
94
|
+
</p>
|
|
95
|
+
)}
|
|
96
|
+
|
|
97
|
+
<Spin spinning={loading}>
|
|
98
|
+
{!loading && matches.length === 0 && (
|
|
99
|
+
<p role="info" className="memori--whythisanswer-no-results">
|
|
100
|
+
{t('nothingFound')}
|
|
101
|
+
</p>
|
|
102
|
+
)}
|
|
103
|
+
{matches.length > 0 && (
|
|
104
|
+
<ul className="memori--whythisanswer-list">
|
|
105
|
+
{matches.map(m => (
|
|
106
|
+
<li key={m.memory.memoryID}>
|
|
107
|
+
<div className="memori--whythisanswer-title">
|
|
108
|
+
<span className="memori--whythisanswer-confidence">
|
|
109
|
+
{m.confidenceLevel}
|
|
110
|
+
</span>
|
|
111
|
+
<div className="memori--whythisanswer-title-text">
|
|
112
|
+
<p>
|
|
113
|
+
<strong>{addQuestionMark(m.memory.title ?? '')}</strong>
|
|
114
|
+
</p>
|
|
115
|
+
<p>
|
|
116
|
+
{m.memory.titleVariants
|
|
117
|
+
?.map(t => addQuestionMark(t))
|
|
118
|
+
?.join(' | ')}
|
|
119
|
+
</p>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
{m.memory.answers?.map((a, i) => (
|
|
123
|
+
<p key={i} className="memori--whythisanswer-answer">
|
|
124
|
+
<Expandable rows={3}>{a.text}</Expandable>
|
|
125
|
+
</p>
|
|
126
|
+
))}
|
|
127
|
+
|
|
128
|
+
{!!m.memory.media?.filter(m => m.mimeType === 'text/plain')
|
|
129
|
+
?.length && (
|
|
130
|
+
<Snippet
|
|
131
|
+
medium={{
|
|
132
|
+
mediumID: m.memory.memoryID,
|
|
133
|
+
mimeType: 'text/plain',
|
|
134
|
+
content: m.memory.media.filter(
|
|
135
|
+
m => m.mimeType === 'text/plain'
|
|
136
|
+
)[0].content,
|
|
137
|
+
}}
|
|
138
|
+
showCopyButton={false}
|
|
139
|
+
showLineNumbers={false}
|
|
140
|
+
/>
|
|
141
|
+
)}
|
|
142
|
+
</li>
|
|
143
|
+
))}
|
|
144
|
+
</ul>
|
|
145
|
+
)}
|
|
146
|
+
</Spin>
|
|
147
|
+
</Drawer>
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export default WhyThisAnswer;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`renders WhyThisAnswer hidden unchanged 1`] = `<div />`;
|
|
4
|
+
|
|
5
|
+
exports[`renders WhyThisAnswer visible unchanged 1`] = `
|
|
6
|
+
<div>
|
|
7
|
+
<div
|
|
8
|
+
style="position: fixed; top: 1px; left: 1px; width: 1px; height: 0px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; border-width: 0px; display: none;"
|
|
9
|
+
/>
|
|
10
|
+
</div>
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
exports[`renders WhyThisAnswer with data unchanged 1`] = `<div />`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
const QuestionHelp = ({
|
|
4
|
+
className,
|
|
5
|
+
title,
|
|
6
|
+
}: {
|
|
7
|
+
className?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
}) => (
|
|
10
|
+
<svg
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
{...(!title ? { 'aria-hidden': 'true' } : {})}
|
|
13
|
+
focusable="false"
|
|
14
|
+
role="img"
|
|
15
|
+
fill="none"
|
|
16
|
+
stroke="currentColor"
|
|
17
|
+
strokeLinecap="round"
|
|
18
|
+
strokeLinejoin="round"
|
|
19
|
+
strokeWidth="1.5"
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
className={className}
|
|
22
|
+
aria-label={title}
|
|
23
|
+
>
|
|
24
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
25
|
+
<path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"></path>
|
|
26
|
+
<path d="M12 17L12.01 17"></path>
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export default QuestionHelp;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
.memori-expandable {
|
|
2
|
+
line-height: 1;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.memori-expandable--inner {
|
|
6
|
+
overflow: hidden;
|
|
7
|
+
max-height: 9999px;
|
|
8
|
+
transition: max-height 0.2s ease-in-out;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.memori-expandable>button {
|
|
12
|
+
color: var(--memori-primary, #333);
|
|
13
|
+
font-size: 0.9em;
|
|
14
|
+
text-decoration: underline
|
|
15
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Meta, Story } from '@storybook/react';
|
|
3
|
+
import Expandable, { Props } from './Expandable';
|
|
4
|
+
|
|
5
|
+
import './Expandable.css';
|
|
6
|
+
|
|
7
|
+
const meta: Meta = {
|
|
8
|
+
title: 'UI/Expandable',
|
|
9
|
+
component: Expandable,
|
|
10
|
+
argTypes: {
|
|
11
|
+
className: {
|
|
12
|
+
control: {
|
|
13
|
+
type: 'text',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
parameters: {
|
|
18
|
+
controls: { expanded: true },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
const Template: Story<Props> = args => (
|
|
25
|
+
<Expandable {...args}>
|
|
26
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
27
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a
|
|
28
|
+
sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
29
|
+
nisi.
|
|
30
|
+
</Expandable>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// By passing using the Args format for exported stories, you can control the props for a component for reuse in a test
|
|
34
|
+
// https://storybook.js.org/docs/react/workflows/unit-testing
|
|
35
|
+
export const Default = Template.bind({});
|
|
36
|
+
Default.args = {
|
|
37
|
+
rows: 2,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const DefaultExpanded = Template.bind({});
|
|
41
|
+
DefaultExpanded.args = {
|
|
42
|
+
rows: 3,
|
|
43
|
+
defaultExpanded: true,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithCustomProps = Template.bind({});
|
|
47
|
+
WithCustomProps.args = {
|
|
48
|
+
rows: 3,
|
|
49
|
+
innerClassName: 'custom-inner-class',
|
|
50
|
+
btnClassName: 'custom-btn-class',
|
|
51
|
+
expandSymbol: () => '🔽',
|
|
52
|
+
collapseSymbol: () => '🔼',
|
|
53
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import Expandable from './Expandable';
|
|
3
|
+
|
|
4
|
+
it('renders Expandable unchanged', () => {
|
|
5
|
+
const { container } = render(<Expandable rows={1}>Test</Expandable>);
|
|
6
|
+
expect(container).toMatchSnapshot();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('renders Expandable with long text unchanged', () => {
|
|
10
|
+
const { container } = render(
|
|
11
|
+
<Expandable rows={1}>
|
|
12
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
13
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
14
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
15
|
+
nisi.
|
|
16
|
+
</Expandable>
|
|
17
|
+
);
|
|
18
|
+
expect(container).toMatchSnapshot();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('renders Expandable with multiple rows unchanged', () => {
|
|
22
|
+
const { container } = render(
|
|
23
|
+
<Expandable rows={3}>
|
|
24
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
25
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
26
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
27
|
+
nisi.
|
|
28
|
+
</Expandable>
|
|
29
|
+
);
|
|
30
|
+
expect(container).toMatchSnapshot();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders Expandable with custom wrapper CSS class unchanged', () => {
|
|
34
|
+
const { container } = render(
|
|
35
|
+
<Expandable rows={1} className="test">
|
|
36
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
37
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
38
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
39
|
+
nisi.
|
|
40
|
+
</Expandable>
|
|
41
|
+
);
|
|
42
|
+
expect(container).toMatchSnapshot();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders Expandable with custom inner CSS class unchanged', () => {
|
|
46
|
+
const { container } = render(
|
|
47
|
+
<Expandable rows={1} innerClassName="test">
|
|
48
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
49
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
50
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
51
|
+
nisi.
|
|
52
|
+
</Expandable>
|
|
53
|
+
);
|
|
54
|
+
expect(container).toMatchSnapshot();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders Expandable with custom button CSS class unchanged', () => {
|
|
58
|
+
const { container } = render(
|
|
59
|
+
<Expandable rows={1} btnClassName="test">
|
|
60
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
61
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
62
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
63
|
+
nisi.
|
|
64
|
+
</Expandable>
|
|
65
|
+
);
|
|
66
|
+
expect(container).toMatchSnapshot();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('renders Expandable with custom expand symbol unchanged', () => {
|
|
70
|
+
const { container } = render(
|
|
71
|
+
<Expandable rows={1} expandSymbol={() => 'test'}>
|
|
72
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
73
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
74
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
75
|
+
nisi.
|
|
76
|
+
</Expandable>
|
|
77
|
+
);
|
|
78
|
+
expect(container).toMatchSnapshot();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('renders Expandable with custom collapse symbol unchanged', () => {
|
|
82
|
+
const { container } = render(
|
|
83
|
+
<Expandable rows={1} collapseSymbol={() => 'test collapse'} defaultExpanded>
|
|
84
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla,
|
|
85
|
+
sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse
|
|
86
|
+
a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper
|
|
87
|
+
nisi.
|
|
88
|
+
</Expandable>
|
|
89
|
+
);
|
|
90
|
+
expect(container).toMatchSnapshot();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('renders Expandable with JSX markup unchanged', () => {
|
|
94
|
+
const { container } = render(
|
|
95
|
+
<Expandable rows={1}>
|
|
96
|
+
<h1>Lorem ipsum</h1>
|
|
97
|
+
<p>Suspendisse a sodales nulla, sed semper nisi.</p>
|
|
98
|
+
<h2>Suspendisse a sodales nulla, sed semper nisi.</h2>
|
|
99
|
+
<p>Suspendisse a sodales nulla, sed semper nisi.</p>
|
|
100
|
+
<p>Suspendisse a sodales nulla, sed semper nisi.</p>
|
|
101
|
+
<button>Dolor sit amet</button>
|
|
102
|
+
<p>Suspendisse a sodales nulla, sed semper nisi.</p>
|
|
103
|
+
<p>Suspendisse a sodales nulla, sed semper nisi.</p>
|
|
104
|
+
</Expandable>
|
|
105
|
+
);
|
|
106
|
+
expect(container).toMatchSnapshot();
|
|
107
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import Button from './Button';
|
|
3
|
+
import cx from 'classnames';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
export interface Props {
|
|
7
|
+
rows: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
innerClassName?: string;
|
|
10
|
+
btnClassName?: string;
|
|
11
|
+
defaultExpanded?: boolean;
|
|
12
|
+
expandSymbol?: (lang: string) => React.ReactNode;
|
|
13
|
+
collapseSymbol?: (lang: string) => React.ReactNode;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const Expandable = ({
|
|
18
|
+
rows,
|
|
19
|
+
className,
|
|
20
|
+
innerClassName,
|
|
21
|
+
btnClassName,
|
|
22
|
+
defaultExpanded = false,
|
|
23
|
+
expandSymbol = () => '...',
|
|
24
|
+
collapseSymbol = (lang: string) =>
|
|
25
|
+
lang === 'it' ? 'Mostra meno' : 'Show less',
|
|
26
|
+
children,
|
|
27
|
+
}: Props) => {
|
|
28
|
+
const { i18n } = useTranslation();
|
|
29
|
+
const lang = i18n.language;
|
|
30
|
+
const collapseSymbolText = collapseSymbol(lang);
|
|
31
|
+
const expandSymbolText = expandSymbol(lang);
|
|
32
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
33
|
+
const [needsExpanding, setNeedsExpanding] = useState(false);
|
|
34
|
+
const [rowHeight, setRowHeight] = useState(16);
|
|
35
|
+
const ref = useRef(null);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (ref.current) {
|
|
39
|
+
let height = (ref.current as HTMLElement).getBoundingClientRect().height;
|
|
40
|
+
let computedStyle = getComputedStyle(ref.current as HTMLElement);
|
|
41
|
+
let elLineHeight = computedStyle.lineHeight;
|
|
42
|
+
let lineHeight =
|
|
43
|
+
elLineHeight === 'normal' || !elLineHeight?.length
|
|
44
|
+
? 1.2 * parseInt(computedStyle.fontSize, 10)
|
|
45
|
+
: parseInt(elLineHeight, 10);
|
|
46
|
+
|
|
47
|
+
console.table({
|
|
48
|
+
rows,
|
|
49
|
+
lineHeight,
|
|
50
|
+
height,
|
|
51
|
+
maxHeight: rows * lineHeight,
|
|
52
|
+
needsExpanding: height > rows * lineHeight,
|
|
53
|
+
});
|
|
54
|
+
setRowHeight(lineHeight);
|
|
55
|
+
if (height && height > rows * lineHeight) {
|
|
56
|
+
setNeedsExpanding(true);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}, [rows, ref.current]);
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className={cx('memori-expandable', className)}>
|
|
63
|
+
<div
|
|
64
|
+
ref={ref}
|
|
65
|
+
className={cx('memori-expandable--inner', innerClassName)}
|
|
66
|
+
style={{
|
|
67
|
+
maxHeight:
|
|
68
|
+
expanded || !needsExpanding ? '9999px' : `${rowHeight * rows}px`,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
</div>
|
|
73
|
+
{needsExpanding && !expanded && (
|
|
74
|
+
<Button
|
|
75
|
+
ghost
|
|
76
|
+
padded={false}
|
|
77
|
+
className={btnClassName}
|
|
78
|
+
onClick={() => setExpanded(true)}
|
|
79
|
+
>
|
|
80
|
+
{expandSymbolText}
|
|
81
|
+
</Button>
|
|
82
|
+
)}
|
|
83
|
+
{needsExpanding && expanded && (
|
|
84
|
+
<Button
|
|
85
|
+
ghost
|
|
86
|
+
padded={false}
|
|
87
|
+
className={btnClassName}
|
|
88
|
+
onClick={() => setExpanded(false)}
|
|
89
|
+
>
|
|
90
|
+
{collapseSymbolText}
|
|
91
|
+
</Button>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export default Expandable;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
table.memori--table {
|
|
2
2
|
--memori-table-spacing: 1rem;
|
|
3
|
-
--memori-table-border-color:
|
|
3
|
+
--memori-table-border-color: rgb(237, 240, 243);
|
|
4
4
|
--memori-table-bg-color: rgb(246, 248, 249);
|
|
5
5
|
|
|
6
6
|
margin: 1rem 0;
|
|
@@ -110,4 +110,4 @@ table.memori--table td.memori--table--column-right {
|
|
|
110
110
|
|
|
111
111
|
.memori--table--date {
|
|
112
112
|
white-space: nowrap;
|
|
113
|
-
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`renders Expandable unchanged 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="memori-expandable"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="memori-expandable--inner"
|
|
10
|
+
style="max-height: 9999px;"
|
|
11
|
+
>
|
|
12
|
+
Test
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
exports[`renders Expandable with JSX markup unchanged 1`] = `
|
|
19
|
+
<div>
|
|
20
|
+
<div
|
|
21
|
+
class="memori-expandable"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
class="memori-expandable--inner"
|
|
25
|
+
style="max-height: 9999px;"
|
|
26
|
+
>
|
|
27
|
+
<h1>
|
|
28
|
+
Lorem ipsum
|
|
29
|
+
</h1>
|
|
30
|
+
<p>
|
|
31
|
+
Suspendisse a sodales nulla, sed semper nisi.
|
|
32
|
+
</p>
|
|
33
|
+
<h2>
|
|
34
|
+
Suspendisse a sodales nulla, sed semper nisi.
|
|
35
|
+
</h2>
|
|
36
|
+
<p>
|
|
37
|
+
Suspendisse a sodales nulla, sed semper nisi.
|
|
38
|
+
</p>
|
|
39
|
+
<p>
|
|
40
|
+
Suspendisse a sodales nulla, sed semper nisi.
|
|
41
|
+
</p>
|
|
42
|
+
<button>
|
|
43
|
+
Dolor sit amet
|
|
44
|
+
</button>
|
|
45
|
+
<p>
|
|
46
|
+
Suspendisse a sodales nulla, sed semper nisi.
|
|
47
|
+
</p>
|
|
48
|
+
<p>
|
|
49
|
+
Suspendisse a sodales nulla, sed semper nisi.
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
exports[`renders Expandable with custom button CSS class unchanged 1`] = `
|
|
57
|
+
<div>
|
|
58
|
+
<div
|
|
59
|
+
class="memori-expandable"
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
class="memori-expandable--inner"
|
|
63
|
+
style="max-height: 9999px;"
|
|
64
|
+
>
|
|
65
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
exports[`renders Expandable with custom collapse symbol unchanged 1`] = `
|
|
72
|
+
<div>
|
|
73
|
+
<div
|
|
74
|
+
class="memori-expandable"
|
|
75
|
+
>
|
|
76
|
+
<div
|
|
77
|
+
class="memori-expandable--inner"
|
|
78
|
+
style="max-height: 9999px;"
|
|
79
|
+
>
|
|
80
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
exports[`renders Expandable with custom expand symbol unchanged 1`] = `
|
|
87
|
+
<div>
|
|
88
|
+
<div
|
|
89
|
+
class="memori-expandable"
|
|
90
|
+
>
|
|
91
|
+
<div
|
|
92
|
+
class="memori-expandable--inner"
|
|
93
|
+
style="max-height: 9999px;"
|
|
94
|
+
>
|
|
95
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
exports[`renders Expandable with custom inner CSS class unchanged 1`] = `
|
|
102
|
+
<div>
|
|
103
|
+
<div
|
|
104
|
+
class="memori-expandable"
|
|
105
|
+
>
|
|
106
|
+
<div
|
|
107
|
+
class="memori-expandable--inner test"
|
|
108
|
+
style="max-height: 9999px;"
|
|
109
|
+
>
|
|
110
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
exports[`renders Expandable with custom wrapper CSS class unchanged 1`] = `
|
|
117
|
+
<div>
|
|
118
|
+
<div
|
|
119
|
+
class="memori-expandable test"
|
|
120
|
+
>
|
|
121
|
+
<div
|
|
122
|
+
class="memori-expandable--inner"
|
|
123
|
+
style="max-height: 9999px;"
|
|
124
|
+
>
|
|
125
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
`;
|
|
130
|
+
|
|
131
|
+
exports[`renders Expandable with long text unchanged 1`] = `
|
|
132
|
+
<div>
|
|
133
|
+
<div
|
|
134
|
+
class="memori-expandable"
|
|
135
|
+
>
|
|
136
|
+
<div
|
|
137
|
+
class="memori-expandable--inner"
|
|
138
|
+
style="max-height: 9999px;"
|
|
139
|
+
>
|
|
140
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
`;
|
|
145
|
+
|
|
146
|
+
exports[`renders Expandable with multiple rows unchanged 1`] = `
|
|
147
|
+
<div>
|
|
148
|
+
<div
|
|
149
|
+
class="memori-expandable"
|
|
150
|
+
>
|
|
151
|
+
<div
|
|
152
|
+
class="memori-expandable--inner"
|
|
153
|
+
style="max-height: 9999px;"
|
|
154
|
+
>
|
|
155
|
+
Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi. Suspendisse a sodales nulla, sed semper nisi.
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
`;
|
package/src/locales/en.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"showTranslatedText": "Show translation",
|
|
34
34
|
"exactPosition": "Exact position",
|
|
35
35
|
"uncertain": "Uncertain",
|
|
36
|
+
"question": "Question",
|
|
36
37
|
"nothingFound": "Nothing found",
|
|
37
38
|
"venue": "Venue",
|
|
38
39
|
"searchVenue": "Search venue...",
|
|
@@ -42,6 +43,8 @@
|
|
|
42
43
|
"memoriBlockedReasonExceedChats": "because it has exceeded the monthly threshold of allowed chats.",
|
|
43
44
|
"memoriBlockedGiverHelper": "You can still manage it as administrator, but other users will not be able to interact with it.",
|
|
44
45
|
"generatedByAI": "Answer generated by AI, may occasionally generate incorrect informations",
|
|
46
|
+
"whyThisAnswer": "Why this answer?",
|
|
47
|
+
"whyThisAnswerHelper": "This answer was generated automatically by an artificial intelligence based on these verified contents.",
|
|
45
48
|
"completionsEnabled": "Advanced AI, can respond with automatically generated answers that may sometimes contain incorrect information",
|
|
46
49
|
"completionProviderDown": "This Twin is integrated with a generative AI from {{provider}}, but it is currently unavailable. Try again later.",
|
|
47
50
|
"completionProviderFallbackName": "an external provider",
|
package/src/locales/it.json
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"showTranslatedText": "Mostra traduzione",
|
|
34
34
|
"exactPosition": "Posizione esatta",
|
|
35
35
|
"uncertain": "Incertezza",
|
|
36
|
+
"question": "Domanda",
|
|
36
37
|
"nothingFound": "Nessun risultato",
|
|
37
38
|
"venue": "Luogo",
|
|
38
39
|
"searchVenue": "Cerca luogo...",
|
|
@@ -42,6 +43,8 @@
|
|
|
42
43
|
"memoriBlockedReasonExceedChats": "perchè ha superato la soglia mensile di chat ammesse.",
|
|
43
44
|
"memoriBlockedGiverHelper": "Puoi sempre gestirlo in qualità di amministratore, ma altri utenti non potranno interrogarlo.",
|
|
44
45
|
"generatedByAI": "Risposta generata da IA, può talvolta generare informazioni non corrette",
|
|
46
|
+
"whyThisAnswer": "Perché questa risposta?",
|
|
47
|
+
"whyThisAnswerHelper": "Questa risposta è stata generata automaticamente da un'intelligenza artificiale sulla base di questi contenuti verificati.",
|
|
45
48
|
"completionsEnabled": "IA evoluta, può rispondere con risposte generate automaticamente che talvolta potrebbero contenere informazioni non corrette",
|
|
46
49
|
"completionProviderDown": "Questo Twin è integrato con una IA generativa di {{provider}}, ma al momento non è disponibile. Riprova più tardi.",
|
|
47
50
|
"completionProviderFallbackName": "un provider esterno",
|