@memori.ai/memori-react 2.1.0 → 2.2.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.
Files changed (106) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/components/Chat/Chat.d.ts +1 -0
  3. package/dist/components/Chat/Chat.js +2 -2
  4. package/dist/components/Chat/Chat.js.map +1 -1
  5. package/dist/components/ChatBubble/ChatBubble.js +1 -1
  6. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  7. package/dist/components/ChatInputs/ChatInputs.css +1 -41
  8. package/dist/components/ChatInputs/ChatInputs.d.ts +1 -0
  9. package/dist/components/ChatInputs/ChatInputs.js +9 -3
  10. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  11. package/dist/components/FeedbackButtons/FeedbackButtons.js +1 -1
  12. package/dist/components/FeedbackButtons/FeedbackButtons.js.map +1 -1
  13. package/dist/components/MemoriWidget/MemoriWidget.js +23 -13
  14. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  15. package/dist/components/MicrophoneButton/MicrophoneButton.css +101 -0
  16. package/dist/components/MicrophoneButton/MicrophoneButton.d.ts +9 -0
  17. package/dist/components/MicrophoneButton/MicrophoneButton.js +46 -0
  18. package/dist/components/MicrophoneButton/MicrophoneButton.js.map +1 -0
  19. package/dist/components/SettingsDrawer/SettingsDrawer.d.ts +3 -3
  20. package/dist/components/SettingsDrawer/SettingsDrawer.js +8 -6
  21. package/dist/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
  22. package/dist/components/SettingsDrawer/SettingsDrawer.test.js +7 -7
  23. package/dist/components/SettingsDrawer/SettingsDrawer.test.js.map +1 -1
  24. package/dist/components/StartPanel/StartPanel.js +1 -1
  25. package/dist/components/StartPanel/StartPanel.js.map +1 -1
  26. package/dist/components/ui/Button.d.ts +5 -1
  27. package/dist/components/ui/Button.js +1 -1
  28. package/dist/components/ui/Button.js.map +1 -1
  29. package/dist/components/ui/Tooltip.css +33 -2
  30. package/dist/components/ui/Tooltip.d.ts +2 -1
  31. package/dist/components/ui/Tooltip.js +1 -2
  32. package/dist/components/ui/Tooltip.js.map +1 -1
  33. package/dist/components/ui/Tooltip.test.js +16 -0
  34. package/dist/components/ui/Tooltip.test.js.map +1 -1
  35. package/dist/helpers/configuration.js +1 -1
  36. package/dist/helpers/configuration.js.map +1 -1
  37. package/dist/locales/en.json +4 -0
  38. package/dist/locales/it.json +4 -0
  39. package/dist/styles.css +3 -2
  40. package/esm/components/Chat/Chat.d.ts +1 -0
  41. package/esm/components/Chat/Chat.js +2 -2
  42. package/esm/components/Chat/Chat.js.map +1 -1
  43. package/esm/components/ChatBubble/ChatBubble.js +1 -1
  44. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  45. package/esm/components/ChatInputs/ChatInputs.css +1 -41
  46. package/esm/components/ChatInputs/ChatInputs.d.ts +1 -0
  47. package/esm/components/ChatInputs/ChatInputs.js +9 -3
  48. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  49. package/esm/components/FeedbackButtons/FeedbackButtons.js +1 -1
  50. package/esm/components/FeedbackButtons/FeedbackButtons.js.map +1 -1
  51. package/esm/components/MemoriWidget/MemoriWidget.js +23 -13
  52. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  53. package/esm/components/MicrophoneButton/MicrophoneButton.css +101 -0
  54. package/esm/components/MicrophoneButton/MicrophoneButton.d.ts +9 -0
  55. package/esm/components/MicrophoneButton/MicrophoneButton.js +43 -0
  56. package/esm/components/MicrophoneButton/MicrophoneButton.js.map +1 -0
  57. package/esm/components/SettingsDrawer/SettingsDrawer.d.ts +3 -3
  58. package/esm/components/SettingsDrawer/SettingsDrawer.js +9 -7
  59. package/esm/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
  60. package/esm/components/SettingsDrawer/SettingsDrawer.test.js +7 -7
  61. package/esm/components/SettingsDrawer/SettingsDrawer.test.js.map +1 -1
  62. package/esm/components/StartPanel/StartPanel.js +1 -1
  63. package/esm/components/StartPanel/StartPanel.js.map +1 -1
  64. package/esm/components/ui/Button.d.ts +5 -1
  65. package/esm/components/ui/Button.js +1 -1
  66. package/esm/components/ui/Button.js.map +1 -1
  67. package/esm/components/ui/Tooltip.css +33 -2
  68. package/esm/components/ui/Tooltip.d.ts +2 -1
  69. package/esm/components/ui/Tooltip.js +1 -2
  70. package/esm/components/ui/Tooltip.js.map +1 -1
  71. package/esm/components/ui/Tooltip.test.js +16 -0
  72. package/esm/components/ui/Tooltip.test.js.map +1 -1
  73. package/esm/helpers/configuration.js +1 -1
  74. package/esm/helpers/configuration.js.map +1 -1
  75. package/esm/locales/en.json +4 -0
  76. package/esm/locales/it.json +4 -0
  77. package/esm/styles.css +3 -2
  78. package/package.json +1 -1
  79. package/src/components/BlockedMemoriBadge/__snapshots__/BlockedMemoriBadge.test.tsx.snap +4 -4
  80. package/src/components/Chat/Chat.tsx +3 -0
  81. package/src/components/ChatBubble/ChatBubble.tsx +1 -1
  82. package/src/components/ChatBubble/__snapshots__/ChatBubble.test.tsx.snap +1 -1
  83. package/src/components/ChatInputs/ChatInputs.css +1 -41
  84. package/src/components/ChatInputs/ChatInputs.stories.tsx +50 -3
  85. package/src/components/ChatInputs/ChatInputs.tsx +20 -3
  86. package/src/components/ChatInputs/__snapshots__/ChatInputs.test.tsx.snap +160 -85
  87. package/src/components/FeedbackButtons/FeedbackButtons.tsx +1 -1
  88. package/src/components/Header/Header.stories.tsx +3 -0
  89. package/src/components/MemoriWidget/MemoriWidget.tsx +29 -12
  90. package/src/components/MicrophoneButton/MicrophoneButton.css +101 -0
  91. package/src/components/MicrophoneButton/MicrophoneButton.stories.tsx +49 -0
  92. package/src/components/MicrophoneButton/MicrophoneButton.tsx +95 -0
  93. package/src/components/SettingsDrawer/SettingsDrawer.stories.tsx +6 -4
  94. package/src/components/SettingsDrawer/SettingsDrawer.test.tsx +14 -14
  95. package/src/components/SettingsDrawer/SettingsDrawer.tsx +57 -25
  96. package/src/components/StartPanel/StartPanel.tsx +3 -3
  97. package/src/components/ui/Button.tsx +21 -1
  98. package/src/components/ui/Tooltip.css +33 -2
  99. package/src/components/ui/Tooltip.stories.tsx +40 -3
  100. package/src/components/ui/Tooltip.test.tsx +52 -0
  101. package/src/components/ui/Tooltip.tsx +12 -7
  102. package/src/components/ui/__snapshots__/Tooltip.test.tsx.snap +80 -4
  103. package/src/helpers/configuration.ts +1 -1
  104. package/src/locales/en.json +4 -0
  105. package/src/locales/it.json +4 -0
  106. package/src/styles.css +3 -2
@@ -0,0 +1,95 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { Props as ChatInputProps } from '../ChatInputs/ChatInputs';
3
+ import Microphone from '../icons/Microphone';
4
+ import Button from '../ui/Button';
5
+ import Tooltip from '../ui/Tooltip';
6
+ import cx from 'classnames';
7
+ import { useTranslation } from 'react-i18next';
8
+
9
+ export interface Props {
10
+ listening?: ChatInputProps['listening'];
11
+ stopAudio: ChatInputProps['stopAudio'];
12
+ startListening: ChatInputProps['startListening'];
13
+ stopListening: ChatInputProps['stopListening'];
14
+ }
15
+
16
+ const MicrophoneButton = ({
17
+ listening,
18
+ stopAudio,
19
+ startListening,
20
+ stopListening,
21
+ }: Props) => {
22
+ const { t } = useTranslation();
23
+ const [micBtnTooltip, setMicBtnTooltip] = useState<string | undefined>();
24
+
25
+ const intervalRef = useRef<any>(null);
26
+
27
+ const startHold = (
28
+ e:
29
+ | React.TouchEvent<HTMLButtonElement>
30
+ | React.MouseEvent<Element, MouseEvent>
31
+ ) => {
32
+ e.preventDefault();
33
+
34
+ setMicBtnTooltip(t('write_and_speak.holdToSpeak') || 'Hold to record');
35
+
36
+ if (intervalRef.current) return;
37
+ intervalRef.current = setTimeout(() => {
38
+ stopAudio();
39
+ setMicBtnTooltip(
40
+ t('write_and_speak.releaseToEndListening') || 'Release to end listening'
41
+ );
42
+ startListening();
43
+ }, 300);
44
+ };
45
+
46
+ const stopHold = () => {
47
+ if (intervalRef.current) {
48
+ clearTimeout(intervalRef.current);
49
+ intervalRef.current = null;
50
+ }
51
+
52
+ stopListening();
53
+ setMicBtnTooltip(undefined);
54
+ };
55
+
56
+ useEffect(() => {
57
+ return () => stopHold();
58
+ }, []);
59
+
60
+ return (
61
+ <Tooltip
62
+ visible={!!micBtnTooltip}
63
+ content={
64
+ <span>
65
+ {micBtnTooltip ||
66
+ t('write_and_speak.pressAndHoldToSpeak') ||
67
+ 'Press and hold to speak'}
68
+ </span>
69
+ }
70
+ align="topLeft"
71
+ className="memori-mic-btn-tooltip"
72
+ >
73
+ <Button
74
+ primary
75
+ className={cx('memori-chat-inputs--mic', {
76
+ 'memori-chat-inputs--mic--listening': listening,
77
+ })}
78
+ title={
79
+ listening
80
+ ? t('write_and_speak.micButtonPopoverListening') || 'Listening'
81
+ : t('write_and_speak.micButtonPopover') || 'Start listening'
82
+ }
83
+ onMouseDown={startHold}
84
+ onTouchStart={startHold}
85
+ onMouseUp={stopHold}
86
+ onMouseLeave={stopHold}
87
+ onTouchEnd={stopHold}
88
+ shape="circle"
89
+ icon={<Microphone />}
90
+ />
91
+ </Tooltip>
92
+ );
93
+ };
94
+
95
+ export default MicrophoneButton;
@@ -21,15 +21,17 @@ const meta: Meta = {
21
21
  export default meta;
22
22
 
23
23
  const Template: Story<Props> = args => {
24
- const [continuousSpeech, setContinuousSpeech] = React.useState(false);
24
+ const [microphoneMode, setMicrophoneMode] = React.useState<
25
+ 'HOLD_TO_TALK' | 'CONTINUOUS'
26
+ >('HOLD_TO_TALK');
25
27
  const [controlsPosition, setControlsPosition] = React.useState<
26
- 'bottom' | 'center' | 'hidden'
28
+ 'bottom' | 'center'
27
29
  >('bottom');
28
30
  return (
29
31
  <SettingsDrawer
30
32
  {...args}
31
- continuousSpeech={continuousSpeech}
32
- setContinuousSpeech={setContinuousSpeech}
33
+ microphoneMode={microphoneMode}
34
+ setMicrophoneMode={setMicrophoneMode}
33
35
  controlsPosition={controlsPosition}
34
36
  setControlsPosition={setControlsPosition}
35
37
  />
@@ -16,9 +16,9 @@ it('renders SettingsDrawer unchanged', () => {
16
16
  <SettingsDrawer
17
17
  open={false}
18
18
  onClose={jest.fn()}
19
- continuousSpeech={false}
19
+ microphoneMode="HOLD_TO_TALK"
20
+ setMicrophoneMode={jest.fn()}
20
21
  continuousSpeechTimeout={2}
21
- setContinuousSpeech={jest.fn()}
22
22
  setContinuousSpeechTimeout={jest.fn()}
23
23
  controlsPosition="bottom"
24
24
  setControlsPosition={jest.fn()}
@@ -34,9 +34,9 @@ it('renders SettingsDrawer open unchanged', () => {
34
34
  <SettingsDrawer
35
35
  open={true}
36
36
  onClose={jest.fn()}
37
- continuousSpeech={false}
37
+ microphoneMode="HOLD_TO_TALK"
38
+ setMicrophoneMode={jest.fn()}
38
39
  continuousSpeechTimeout={2}
39
- setContinuousSpeech={jest.fn()}
40
40
  setContinuousSpeechTimeout={jest.fn()}
41
41
  controlsPosition="bottom"
42
42
  setControlsPosition={jest.fn()}
@@ -52,9 +52,9 @@ it('renders SettingsDrawer open with continuous speech enabled unchanged', () =>
52
52
  <SettingsDrawer
53
53
  open={true}
54
54
  onClose={jest.fn()}
55
- continuousSpeech={true}
55
+ microphoneMode="CONTINUOUS"
56
+ setMicrophoneMode={jest.fn()}
56
57
  continuousSpeechTimeout={2}
57
- setContinuousSpeech={jest.fn()}
58
58
  setContinuousSpeechTimeout={jest.fn()}
59
59
  controlsPosition="bottom"
60
60
  setControlsPosition={jest.fn()}
@@ -70,9 +70,9 @@ it('renders SettingsDrawer open with non-default continuous speech timeout uncha
70
70
  <SettingsDrawer
71
71
  open={true}
72
72
  onClose={jest.fn()}
73
- continuousSpeech={false}
73
+ microphoneMode="CONTINUOUS"
74
+ setMicrophoneMode={jest.fn()}
74
75
  continuousSpeechTimeout={10}
75
- setContinuousSpeech={jest.fn()}
76
76
  setContinuousSpeechTimeout={jest.fn()}
77
77
  controlsPosition="bottom"
78
78
  setControlsPosition={jest.fn()}
@@ -89,9 +89,9 @@ it('renders SettingsDrawer for totem layout open unchanged', () => {
89
89
  layout="TOTEM"
90
90
  open={true}
91
91
  onClose={jest.fn()}
92
- continuousSpeech={false}
92
+ microphoneMode="HOLD_TO_TALK"
93
+ setMicrophoneMode={jest.fn()}
93
94
  continuousSpeechTimeout={2}
94
- setContinuousSpeech={jest.fn()}
95
95
  setContinuousSpeechTimeout={jest.fn()}
96
96
  controlsPosition="bottom"
97
97
  setControlsPosition={jest.fn()}
@@ -108,9 +108,9 @@ it('renders SettingsDrawer for totem layout open with controls at center unchang
108
108
  layout="TOTEM"
109
109
  open={true}
110
110
  onClose={jest.fn()}
111
- continuousSpeech={false}
111
+ microphoneMode="HOLD_TO_TALK"
112
+ setMicrophoneMode={jest.fn()}
112
113
  continuousSpeechTimeout={2}
113
- setContinuousSpeech={jest.fn()}
114
114
  setContinuousSpeechTimeout={jest.fn()}
115
115
  controlsPosition="center"
116
116
  setControlsPosition={jest.fn()}
@@ -127,9 +127,9 @@ it('renders SettingsDrawer for totem layout with continuous speech and hide emis
127
127
  layout="TOTEM"
128
128
  open={true}
129
129
  onClose={jest.fn()}
130
- continuousSpeech={true}
130
+ microphoneMode="HOLD_TO_TALK"
131
+ setMicrophoneMode={jest.fn()}
131
132
  continuousSpeechTimeout={2}
132
- setContinuousSpeech={jest.fn()}
133
133
  setContinuousSpeechTimeout={jest.fn()}
134
134
  controlsPosition="bottom"
135
135
  setControlsPosition={jest.fn()}
@@ -11,9 +11,9 @@ export interface Props {
11
11
  open: boolean;
12
12
  layout?: 'FULLPAGE' | 'TOTEM' | 'DEFAULT';
13
13
  onClose: () => void;
14
- continuousSpeech?: boolean;
14
+ microphoneMode?: 'HOLD_TO_TALK' | 'CONTINUOUS';
15
15
  continuousSpeechTimeout?: number;
16
- setContinuousSpeech: (value: boolean) => void;
16
+ setMicrophoneMode: (value: 'HOLD_TO_TALK' | 'CONTINUOUS') => void;
17
17
  setContinuousSpeechTimeout: (value: number) => void;
18
18
  controlsPosition?: 'center' | 'bottom';
19
19
  setControlsPosition: (value: 'center' | 'bottom') => void;
@@ -27,9 +27,9 @@ const SettingsDrawer = ({
27
27
  open,
28
28
  layout = 'DEFAULT',
29
29
  onClose,
30
- continuousSpeech,
30
+ microphoneMode = 'HOLD_TO_TALK',
31
31
  continuousSpeechTimeout,
32
- setContinuousSpeech,
32
+ setMicrophoneMode,
33
33
  setContinuousSpeechTimeout,
34
34
  controlsPosition,
35
35
  setControlsPosition,
@@ -46,31 +46,63 @@ const SettingsDrawer = ({
46
46
  title={t('widget.settings') || 'Settings'}
47
47
  description={t('write_and_speak.settingsHeaderLabel')}
48
48
  >
49
- <div className="memori-settings-drawer--field">
50
- <Checkbox
51
- label={t('write_and_speak.continuousSpeechLabel')}
52
- name="continuousSpeech"
53
- checked={continuousSpeech}
54
- onChange={e => {
55
- setContinuousSpeech(e.target.checked);
56
- setLocalConfig('continuousSpeech', e.target.checked);
57
- }}
58
- />
59
- </div>
60
-
61
- <div className="memori-settings-drawer--field">
62
- <Select
63
- label={t('write_and_speak.secondsLabel') || 'Seconds'}
64
- placeholder={t('write_and_speak.secondsLabel') || 'Seconds'}
65
- options={silenceSeconds.map(s => ({ value: s, label: s }))}
66
- value={continuousSpeechTimeout}
49
+ <div className="memori-settings-drawer--field controls">
50
+ <label htmlFor="#microphoneMode">
51
+ {t('write_and_speak.microphoneMode') || 'Microphone mode'}:
52
+ </label>
53
+ <RadioGroup
54
+ id="microphoneMode"
55
+ name="microphoneMode"
56
+ value={microphoneMode}
57
+ defaultValue={microphoneMode}
58
+ className="memori-settings-drawer--microphoneMode-radio"
67
59
  onChange={value => {
68
- setContinuousSpeechTimeout(value);
69
- setLocalConfig('continuousSpeechTimeout', value);
60
+ let micMode =
61
+ value === 'CONTINUOUS' ? 'CONTINUOUS' : 'HOLD_TO_TALK';
62
+
63
+ setMicrophoneMode(micMode as 'CONTINUOUS' | 'HOLD_TO_TALK');
64
+ setLocalConfig('microphoneMode', micMode);
70
65
  }}
71
- />
66
+ >
67
+ <RadioGroup.Option
68
+ value="HOLD_TO_TALK"
69
+ className="memori-settings-drawer--microphoneMode-radio-button"
70
+ >
71
+ {({ checked }) => (
72
+ <Button primary={checked}>
73
+ {t('write_and_speak.holdToSpeak') || 'Hold to speak'}
74
+ </Button>
75
+ )}
76
+ </RadioGroup.Option>
77
+ <RadioGroup.Option
78
+ value="CONTINUOUS"
79
+ className="memori-settings-drawer--microphoneMode-radio-button"
80
+ >
81
+ {({ checked }) => (
82
+ <Button primary={checked}>
83
+ {t('write_and_speak.continuousSpeechLabel') ||
84
+ 'Continuous speech'}
85
+ </Button>
86
+ )}
87
+ </RadioGroup.Option>
88
+ </RadioGroup>
72
89
  </div>
73
90
 
91
+ {microphoneMode === 'CONTINUOUS' && (
92
+ <div className="memori-settings-drawer--field">
93
+ <Select
94
+ label={t('write_and_speak.secondsLabel') || 'Seconds'}
95
+ placeholder={t('write_and_speak.secondsLabel') || 'Seconds'}
96
+ options={silenceSeconds.map(s => ({ value: s, label: s }))}
97
+ value={continuousSpeechTimeout}
98
+ onChange={value => {
99
+ setContinuousSpeechTimeout(value);
100
+ setLocalConfig('continuousSpeechTimeout', value);
101
+ }}
102
+ />
103
+ </div>
104
+ )}
105
+
74
106
  {layout === 'TOTEM' && (
75
107
  <>
76
108
  <div className="memori-settings-drawer--field controls">
@@ -102,7 +102,7 @@ const StartPanel: React.FC<Props> = ({
102
102
  {!!gamificationLevel?.badge?.length && (
103
103
  <div className="memori--gamification-badge">
104
104
  <Tooltip
105
- alignLeft
105
+ align="left"
106
106
  content={`${t('gamification.level')} ${
107
107
  gamificationLevel.badge
108
108
  }, ${gamificationLevel.points} ${t('gamification.points')}`}
@@ -119,7 +119,7 @@ const StartPanel: React.FC<Props> = ({
119
119
  )}
120
120
  {!!memori.enableCompletions && (
121
121
  <div className="memori--completions-enabled">
122
- <Tooltip alignLeft content={t('completionsEnabled')}>
122
+ <Tooltip align="left" content={t('completionsEnabled')}>
123
123
  <span aria-label={t('completionsEnabled') || 'Completions'}>
124
124
  <AI />
125
125
  </span>
@@ -128,7 +128,7 @@ const StartPanel: React.FC<Props> = ({
128
128
  )}
129
129
  {!!memori.nsfw && (
130
130
  <div className="memori--nsfw">
131
- <Tooltip alignLeft content={t('nsfw')}>
131
+ <Tooltip align="left" content={t('nsfw')}>
132
132
  <span title={t('nsfw') || 'NSFW'}>🔞</span>
133
133
  </Tooltip>
134
134
  </div>
@@ -23,7 +23,19 @@ export interface Props {
23
23
  id?: string;
24
24
  htmlType?: 'button' | 'submit' | 'reset';
25
25
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
26
- onTouchEnd?: (event: React.TouchEvent<HTMLButtonElement>) => void;
26
+ onMouseDown?: (
27
+ event: React.MouseEvent<HTMLButtonElement, MouseEvent>
28
+ ) => void;
29
+ onMouseUp?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
30
+ onMouseLeave?: (
31
+ event: React.MouseEvent<HTMLButtonElement, MouseEvent>
32
+ ) => void;
33
+ onTouchStart?: (
34
+ event: React.TouchEvent<HTMLButtonElement> | React.MouseEvent
35
+ ) => void;
36
+ onTouchEnd?: (
37
+ event: React.TouchEvent<HTMLButtonElement> | React.MouseEvent
38
+ ) => void;
27
39
  }
28
40
 
29
41
  const Button: FC<Props> = ({
@@ -42,6 +54,10 @@ const Button: FC<Props> = ({
42
54
  id,
43
55
  htmlType,
44
56
  onClick,
57
+ onMouseDown,
58
+ onMouseUp,
59
+ onMouseLeave,
60
+ onTouchStart,
45
61
  onTouchEnd,
46
62
  children,
47
63
  }) => (
@@ -49,6 +65,10 @@ const Button: FC<Props> = ({
49
65
  id={id}
50
66
  type={htmlType}
51
67
  onClick={onClick}
68
+ onMouseDown={onMouseDown}
69
+ onMouseUp={onMouseUp}
70
+ onMouseLeave={onMouseLeave}
71
+ onTouchStart={onTouchStart}
52
72
  onTouchEnd={onTouchEnd}
53
73
  title={title}
54
74
  disabled={loading || disabled}
@@ -1,3 +1,5 @@
1
+ /* stylelint-disable no-descending-specificity */
2
+
1
3
  .memori-tooltip {
2
4
  position: relative;
3
5
  display: inline-block;
@@ -23,7 +25,8 @@
23
25
  transition: 0.3s 0.1s all ease;
24
26
  }
25
27
 
26
- .memori-tooltip.memori-tooltip--align-left .memori-tooltip--content {
28
+ .memori-tooltip.memori-tooltip--align-left .memori-tooltip--content,
29
+ .memori-tooltip.memori-tooltip--align-topLeft .memori-tooltip--content {
27
30
  right: 100%;
28
31
  left: initial;
29
32
  margin: initial;
@@ -52,6 +55,15 @@
52
55
  margin-left: 0;
53
56
  }
54
57
 
58
+ .memori-tooltip.memori-tooltip--align-topRight .memori-tooltip--content::before {
59
+ top: 100%;
60
+ right: auto;
61
+ left: 10px;
62
+ border-color: rgba(0, 0, 0, 0.8) transparent transparent transparent;
63
+ margin-top: 10px;
64
+ margin-left: 5px;
65
+ }
66
+
55
67
  .memori-tooltip:not(.memori-tooltip--disabled).memori-tooltip--visible .memori-tooltip--content,
56
68
  .memori-tooltip:not(.memori-tooltip--disabled).memori-tooltip--visible .memori-tooltip--content::before {
57
69
  display: block;
@@ -69,6 +81,25 @@
69
81
  opacity: 1;
70
82
  }
71
83
 
84
+ .memori-tooltip.memori-tooltip--align-topRight:not(.memori-tooltip--disabled).memori-tooltip--visible .memori-tooltip--content,
85
+ .memori-tooltip.memori-tooltip--align-topRight:not(.memori-tooltip--disabled):not(.memori-tooltip--visible):hover .memori-tooltip--content {
86
+ transform: translateY(-150%) translateX(-33%);
87
+ }
88
+
89
+ .memori-tooltip.memori-tooltip--align-topLeft .memori-tooltip--content::before {
90
+ top: 100%;
91
+ right: 10px;
92
+ left: auto;
93
+ border-color: rgba(0, 0, 0, 0.8) transparent transparent transparent;
94
+ margin-top: 10px;
95
+ margin-left: 5px;
96
+ }
97
+
98
+ .memori-tooltip.memori-tooltip--align-topLeft:not(.memori-tooltip--disabled).memori-tooltip--visible .memori-tooltip--content,
99
+ .memori-tooltip.memori-tooltip--align-topLeft:not(.memori-tooltip--disabled):not(.memori-tooltip--visible):hover .memori-tooltip--content {
100
+ transform: translateY(-150%) translateX(33%);
101
+ }
102
+
72
103
  .memori-tooltip:not(.memori-tooltip--disabled).memori-tooltip--visible .memori-tooltip--content a,
73
104
  .memori-tooltip:not(.memori-tooltip--disabled):not(.memori-tooltip--visible):hover .memori-tooltip--content a {
74
105
  color: #fff;
@@ -77,4 +108,4 @@
77
108
 
78
109
  .memori-tooltip.memori-tooltip--disabled {
79
110
  cursor: not-allowed;
80
- }
111
+ }
@@ -20,9 +20,21 @@ const meta: Meta = {
20
20
 
21
21
  export default meta;
22
22
 
23
- const Template: Story<Props> = args => <Tooltip {...args} />;
23
+ const Template: Story<Props> = args => (
24
+ <div
25
+ style={{
26
+ display: 'flex',
27
+ justifyContent: 'flex-start',
28
+ paddingTop: '10rem',
29
+ }}
30
+ >
31
+ <Tooltip {...args} />
32
+ </div>
33
+ );
24
34
  const TemplateRight: Story<Props> = args => (
25
- <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
35
+ <div
36
+ style={{ display: 'flex', justifyContent: 'flex-end', paddingTop: '10rem' }}
37
+ >
26
38
  <Tooltip {...args} />
27
39
  </div>
28
40
  );
@@ -68,5 +80,30 @@ export const AlignLeft = TemplateRight.bind({});
68
80
  AlignLeft.args = {
69
81
  content: 'Here is some tooltip content',
70
82
  children: <span>Hover me</span>,
71
- alignLeft: true,
83
+ align: 'left',
84
+ visible: true,
85
+ };
86
+
87
+ export const AlignRight = Template.bind({});
88
+ AlignRight.args = {
89
+ content: 'Here is some tooltip content',
90
+ children: <span>Hover me</span>,
91
+ align: 'right',
92
+ visible: true,
93
+ };
94
+
95
+ export const AlignTopLeft = TemplateRight.bind({});
96
+ AlignTopLeft.args = {
97
+ content: 'Here is some tooltip content',
98
+ children: <span>Hover me</span>,
99
+ align: 'topLeft',
100
+ visible: true,
101
+ };
102
+
103
+ export const AlignTopRight = Template.bind({});
104
+ AlignTopRight.args = {
105
+ content: 'Here is some tooltip content',
106
+ children: <span>Hover me</span>,
107
+ align: 'topRight',
108
+ visible: true,
72
109
  };
@@ -48,3 +48,55 @@ it('renders Tooltip visible unchanged', () => {
48
48
  );
49
49
  expect(container).toMatchSnapshot();
50
50
  });
51
+
52
+ it('renders Tooltip aligned left unchanged', () => {
53
+ const { container } = render(
54
+ <Tooltip
55
+ content="Here is some tooltip content by default as I am controlled"
56
+ visible
57
+ align="left"
58
+ >
59
+ Hover me
60
+ </Tooltip>
61
+ );
62
+ expect(container).toMatchSnapshot();
63
+ });
64
+
65
+ it('renders Tooltip aligned top left unchanged', () => {
66
+ const { container } = render(
67
+ <Tooltip
68
+ content="Here is some tooltip content by default as I am controlled"
69
+ visible
70
+ align="topLeft"
71
+ >
72
+ Hover me
73
+ </Tooltip>
74
+ );
75
+ expect(container).toMatchSnapshot();
76
+ });
77
+
78
+ it('renders Tooltip aligned right unchanged', () => {
79
+ const { container } = render(
80
+ <Tooltip
81
+ content="Here is some tooltip content by default as I am controlled"
82
+ visible
83
+ align="right"
84
+ >
85
+ Hover me
86
+ </Tooltip>
87
+ );
88
+ expect(container).toMatchSnapshot();
89
+ });
90
+
91
+ it('renders Tooltip aligned top right unchanged', () => {
92
+ const { container } = render(
93
+ <Tooltip
94
+ content="Here is some tooltip content by default as I am controlled"
95
+ visible
96
+ align="topRight"
97
+ >
98
+ Hover me
99
+ </Tooltip>
100
+ );
101
+ expect(container).toMatchSnapshot();
102
+ });
@@ -4,7 +4,8 @@ import cx from 'classnames';
4
4
  export interface Props {
5
5
  content: string | JSX.Element | React.ReactNode;
6
6
  className?: string;
7
- alignLeft?: boolean;
7
+ align?: 'left' | 'right' | 'topLeft' | 'topRight';
8
+ alignTop?: boolean;
8
9
  disabled?: boolean;
9
10
  children: React.ReactNode;
10
11
  visible?: boolean;
@@ -13,17 +14,21 @@ export interface Props {
13
14
  const Tooltip: FC<Props> = ({
14
15
  content,
15
16
  className,
16
- alignLeft = false,
17
+ align = 'right',
17
18
  disabled = false,
18
19
  visible = false,
19
20
  children,
20
21
  }) => (
21
22
  <div
22
- className={cx('memori-tooltip', className, {
23
- 'memori-tooltip--align-left': alignLeft,
24
- 'memori-tooltip--disabled': disabled,
25
- 'memori-tooltip--visible': visible,
26
- })}
23
+ className={cx(
24
+ 'memori-tooltip',
25
+ `memori-tooltip--align-${align}`,
26
+ className,
27
+ {
28
+ 'memori-tooltip--disabled': disabled,
29
+ 'memori-tooltip--visible': visible,
30
+ }
31
+ )}
27
32
  >
28
33
  <div className="memori-tooltip--content">{content}</div>
29
34
  <div className="memori-tooltip--trigger">{children}</div>