@mindly/ui-components 3.1.4 → 3.1.5
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/dist/fonts/Lato-Bold.ttf +0 -0
- package/dist/fonts/Lato-Regular.ttf +0 -0
- package/dist/fonts/Lato-Semibold.ttf +0 -0
- package/dist/index.ts +106 -0
- package/dist/lib/AppHeader/AppHeader.style.ts +19 -0
- package/dist/lib/AppHeader/AppHeader.tsx +23 -0
- package/dist/lib/AppHeader/index.ts +1 -0
- package/dist/lib/Avatar/Avatar.style.ts +17 -0
- package/dist/lib/Avatar/Avatar.tsx +46 -0
- package/dist/lib/Avatar/index.ts +1 -0
- package/dist/lib/Container/Container.styled.ts +15 -0
- package/dist/lib/Container/Container.tsx +15 -0
- package/dist/lib/Container/index.ts +1 -0
- package/dist/lib/EntryNotFound/EntryNotFound.style.ts +14 -0
- package/dist/lib/EntryNotFound/EntryNotFound.tsx +21 -0
- package/dist/lib/EntryNotFound/calendar.svg +69 -0
- package/dist/lib/EntryNotFound/index.ts +1 -0
- package/dist/lib/Filters/ListSelect/ListSelect.style.ts +38 -0
- package/dist/lib/Filters/ListSelect/ListSelect.tsx +48 -0
- package/dist/lib/Filters/ListSelect/index.ts +1 -0
- package/dist/lib/Filters/Range/Range.style.ts +41 -0
- package/dist/lib/Filters/Range/Range.tsx +48 -0
- package/dist/lib/Filters/Range/index.ts +1 -0
- package/dist/lib/Filters/RowSelect/RowSelect.style.ts +30 -0
- package/dist/lib/Filters/RowSelect/RowSelect.tsx +38 -0
- package/dist/lib/Filters/RowSelect/index.ts +1 -0
- package/dist/lib/Filters/Toggle/Toggle.style.ts +14 -0
- package/dist/lib/Filters/Toggle/Toggle.tsx +26 -0
- package/dist/lib/Filters/Toggle/index.ts +1 -0
- package/dist/lib/HorisontalCalendar/HorizontalCalendar.styled.ts +117 -0
- package/dist/lib/HorisontalCalendar/HorizontalCalendar.tsx +213 -0
- package/dist/lib/HorisontalCalendar/index.ts +1 -0
- package/dist/lib/ImageWithFallback/ImageWithFallback.tsx +37 -0
- package/dist/lib/LetterAvatar/LetterAvatar.styled.ts +32 -0
- package/dist/lib/LetterAvatar/LetterAvatar.tsx +23 -0
- package/dist/lib/Modal/Modal.style.ts +12 -0
- package/dist/lib/Modal/Modal.tsx +30 -0
- package/dist/lib/Modal/index.ts +1 -0
- package/dist/lib/ModalCalendar/ModalCalendar.styled.ts +123 -0
- package/dist/lib/ModalCalendar/ModalCalendar.tsx +71 -0
- package/dist/lib/PersonDateTimeCard/PersonDateTimeCard.styled.ts +36 -0
- package/dist/lib/PersonDateTimeCard/PersonDateTimeCard.tsx +32 -0
- package/dist/lib/Segment/Segment.style.ts +14 -0
- package/dist/lib/Segment/Segment.tsx +29 -0
- package/dist/lib/Segment/index.ts +2 -0
- package/dist/lib/Segment/types.ts +4 -0
- package/dist/lib/SelectImpressionEmoji/ImpressionEmojiEnum.ts +9 -0
- package/dist/lib/SelectImpressionEmoji/SelectImpressionEmoji.styled.ts +28 -0
- package/dist/lib/SelectImpressionEmoji/SelectImpressionEmoji.tsx +56 -0
- package/dist/lib/SelectImpressionEmoji/SelectImpressionEmojiProps.ts +8 -0
- package/dist/lib/SelectImpressionEmoji/emojis.ts +9 -0
- package/dist/lib/SelectImpressionEmoji/index.tsx +14 -0
- package/dist/lib/Theme/global.css +251 -0
- package/dist/lib/Theme/mindly_constants.ts +23 -0
- package/dist/lib/UsersPsychologistScrollList/UserPsychologistScrollList.styled.ts +61 -0
- package/dist/lib/UsersPsychologistScrollList/UsersPsychologistScrollList.tsx +68 -0
- package/dist/lib/archived-consultation-card/ArchivedConsultation.test.tsx +9 -0
- package/dist/lib/archived-consultation-card/ArchivedConsultationCard.style.ts +72 -0
- package/dist/lib/archived-consultation-card/ArchivedConsultationCard.svg +3 -0
- package/dist/lib/archived-consultation-card/ArchivedConsultationCard.tsx +61 -0
- package/dist/lib/button/Button.style.ts +170 -0
- package/dist/lib/button/Button.test.tsx +39 -0
- package/dist/lib/button/Button.tsx +47 -0
- package/dist/lib/consultation-card/ConsultationCard.style.ts +119 -0
- package/dist/lib/consultation-card/ConsultationCard.test.tsx +65 -0
- package/dist/lib/consultation-card/ConsultationCard.tsx +155 -0
- package/dist/lib/consultation-card/ConsultationCardSkeleton.tsx +114 -0
- package/dist/lib/consultation-card/index.ts +2 -0
- package/dist/lib/content-card/ContentCard.style.ts +59 -0
- package/dist/lib/content-card/ContentCard.test.tsx +29 -0
- package/dist/lib/content-card/ContentCard.tsx +81 -0
- package/dist/lib/date-picker/DatePicker.style.ts +52 -0
- package/dist/lib/date-picker/DatePicker.test.tsx +9 -0
- package/dist/lib/date-picker/DatePicker.tsx +59 -0
- package/dist/lib/floating-button/FloatingButton.style.ts +21 -0
- package/dist/lib/floating-button/FloatingButton.test.tsx +9 -0
- package/dist/lib/floating-button/FloatingButton.tsx +29 -0
- package/dist/lib/floating-button/floating button.svg +6 -0
- package/dist/lib/footer-for-booking/FooterForBooking.style.ts +56 -0
- package/dist/lib/footer-for-booking/FooterForBooking.test.tsx +30 -0
- package/dist/lib/footer-for-booking/FooterForBooking.tsx +53 -0
- package/dist/lib/input/Input.style.ts +37 -0
- package/dist/lib/input/Input.test.tsx +21 -0
- package/dist/lib/input/Input.tsx +73 -0
- package/dist/lib/list-button/ListButton.style.ts +21 -0
- package/dist/lib/list-button/ListButton.test.tsx +26 -0
- package/dist/lib/list-button/ListButton.tsx +30 -0
- package/dist/lib/navigation-bar/NavigationBar.style.ts +81 -0
- package/dist/lib/navigation-bar/NavigationBar.test.tsx +15 -0
- package/dist/lib/navigation-bar/NavigationBar.tsx +31 -0
- package/dist/lib/no-internet-connection/NoInternetConnection.style.ts +26 -0
- package/dist/lib/no-internet-connection/NoInternetConnection.svg +10 -0
- package/dist/lib/no-internet-connection/NoInternetConnection.test.tsx +9 -0
- package/dist/lib/no-internet-connection/NoInternetConnection.tsx +30 -0
- package/dist/lib/notes-card-text/NotesCardText.style.ts +14 -0
- package/dist/lib/notes-card-text/NotesCardText.tsx +32 -0
- package/dist/lib/notes-card-text/index.ts +1 -0
- package/dist/lib/notes-editor/NotesEditor.styled.ts +24 -0
- package/dist/lib/notes-editor/NotesEditor.tsx +16 -0
- package/dist/lib/scroll-tabs/ScrollTabs.style.ts +19 -0
- package/dist/lib/scroll-tabs/ScrollTabs.test.tsx +9 -0
- package/dist/lib/scroll-tabs/ScrollTabs.tsx +42 -0
- package/dist/lib/tab-bar/TabBar.style.tsx +43 -0
- package/dist/lib/tab-bar/TabBar.tsx +11 -0
- package/dist/lib/therapist-card/TherapistCard.style.ts +104 -0
- package/dist/lib/therapist-card/TherapistCard.test.tsx +40 -0
- package/dist/lib/therapist-card/TherapistCard.tsx +96 -0
- package/dist/lib/therapist-information-component/TherapistInformationComponent.style.ts +40 -0
- package/dist/lib/therapist-information-component/TherapistInformationComponent.test.tsx +16 -0
- package/dist/lib/therapist-information-component/TherapistInformationComponent.tsx +51 -0
- package/dist/lib/toast/index.css +13 -0
- package/dist/lib/toast/toast.ts +17 -0
- package/dist/lib/userAppTypes.ts +261 -0
- package/dist/lib/your-local-time-block/YourLocalTimeBlock.styled.ts +28 -0
- package/dist/lib/your-local-time-block/YourLocalTimeBlock.tsx +26 -0
- package/dist/react-app-env.d.ts +1 -0
- package/dist/svg.d.ts +13 -0
- package/package.json +4 -17
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { colorConstants } from '../Theme/mindly_constants';
|
|
3
|
+
import { IonSkeletonText } from '@ionic/react';
|
|
4
|
+
|
|
5
|
+
export const Container = styled.div`
|
|
6
|
+
border: 1px solid ${colorConstants.StrokeGray};
|
|
7
|
+
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.1);
|
|
8
|
+
border-radius: 8px;
|
|
9
|
+
position: relative;
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: column;
|
|
12
|
+
text-align: left;
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const ContentArticle = styled.a`
|
|
16
|
+
width: 100%;
|
|
17
|
+
min-height: 192px;
|
|
18
|
+
max-height: 192px;
|
|
19
|
+
display: block;
|
|
20
|
+
|
|
21
|
+
img {
|
|
22
|
+
border-radius: 8px 8px 0 0;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 192px;
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
export const ContentVideo = styled.iframe`
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 192px;
|
|
31
|
+
margin: 0;
|
|
32
|
+
padding: 0;
|
|
33
|
+
border-radius: 8px 8px 0 0;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
export const Title = styled.h3`
|
|
37
|
+
padding: 16px !important;
|
|
38
|
+
height: 104px;
|
|
39
|
+
color: ${colorConstants.primaryTextColor};
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
interface SkeletonProps {
|
|
43
|
+
isLoading: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const SkeletonWrapper = styled.div<SkeletonProps>`
|
|
47
|
+
visibility: ${(props) => (props.isLoading ? 'visible' : 'hidden')};
|
|
48
|
+
position: absolute;
|
|
49
|
+
width: 100%;
|
|
50
|
+
top: 0;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
export const Skeleton = styled(IonSkeletonText)`
|
|
54
|
+
height: 192px;
|
|
55
|
+
border-radius: 4px 4px 0 0;
|
|
56
|
+
border: none;
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { ContentCard } from './ContentCard';
|
|
4
|
+
|
|
5
|
+
describe('Test content card', () => {
|
|
6
|
+
test('Smoke test video', () => {
|
|
7
|
+
render(
|
|
8
|
+
<ContentCard
|
|
9
|
+
contentVideo={{
|
|
10
|
+
title: 'КАК ВЫЙТИ ИЗ ДЕПРЕССИИ: СОВЕТЫ ПСИХОЛОГА',
|
|
11
|
+
url: 'watch?v=GnrkrxNL1Ek',
|
|
12
|
+
}}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('Smoke test articles', () => {
|
|
18
|
+
render(
|
|
19
|
+
<ContentCard
|
|
20
|
+
contentArticle={{
|
|
21
|
+
link: 'https://ru.mindly.cc/our-thoughts-our-friends-or-enemies/',
|
|
22
|
+
photoURL:
|
|
23
|
+
'https://ru.mindly.cc/content/images/size/w2000/2020/10/cover-prepared-1--2-.jpg',
|
|
24
|
+
title: 'Мысли - наши друзья или наши враги?',
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Container,
|
|
4
|
+
ContentArticle,
|
|
5
|
+
ContentVideo,
|
|
6
|
+
Skeleton,
|
|
7
|
+
SkeletonWrapper,
|
|
8
|
+
Title,
|
|
9
|
+
} from './ContentCard.style';
|
|
10
|
+
import '../Theme/global.css';
|
|
11
|
+
|
|
12
|
+
export interface ContentCardProps {
|
|
13
|
+
/*
|
|
14
|
+
* We pass this object when we need to create a card with a video
|
|
15
|
+
* title: video title
|
|
16
|
+
* url: link to video
|
|
17
|
+
*/
|
|
18
|
+
contentVideo?: {
|
|
19
|
+
title: string;
|
|
20
|
+
url: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* We pass this object when we need to create a card with a article
|
|
25
|
+
* title: article title
|
|
26
|
+
* link: link to article
|
|
27
|
+
* photoURL: link to article preview
|
|
28
|
+
*/
|
|
29
|
+
contentArticle?: {
|
|
30
|
+
link: string;
|
|
31
|
+
photoURL: string;
|
|
32
|
+
title: string;
|
|
33
|
+
};
|
|
34
|
+
['data-testid']?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const ContentCard: React.FC<ContentCardProps> = (props) => {
|
|
38
|
+
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
39
|
+
const title = props.contentVideo?.title || props.contentArticle?.title;
|
|
40
|
+
const contentJSX = props.contentArticle ? (
|
|
41
|
+
<ContentArticle
|
|
42
|
+
data-testID={props['data-testid']}
|
|
43
|
+
href={props.contentArticle.link}
|
|
44
|
+
>
|
|
45
|
+
<img
|
|
46
|
+
src={props.contentArticle.photoURL}
|
|
47
|
+
alt="article-img"
|
|
48
|
+
onLoad={() => {
|
|
49
|
+
setIsLoading(false);
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
</ContentArticle>
|
|
53
|
+
) : (
|
|
54
|
+
<ContentVideo
|
|
55
|
+
data-testid={props['data-testid']}
|
|
56
|
+
src={`https://www.youtube.com/embed/${props.contentVideo?.url}`}
|
|
57
|
+
title="YouTube video player"
|
|
58
|
+
frameBorder="0"
|
|
59
|
+
onLoad={() => {
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}}
|
|
62
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
63
|
+
allowFullScreen
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
return (
|
|
67
|
+
<Container>
|
|
68
|
+
{contentJSX}
|
|
69
|
+
<SkeletonWrapper isLoading={isLoading}>
|
|
70
|
+
<Skeleton animated />
|
|
71
|
+
</SkeletonWrapper>
|
|
72
|
+
<Title className="semiBold">
|
|
73
|
+
{' '}
|
|
74
|
+
{
|
|
75
|
+
// #skipcq
|
|
76
|
+
}
|
|
77
|
+
{title}
|
|
78
|
+
</Title>
|
|
79
|
+
</Container>
|
|
80
|
+
);
|
|
81
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { colorConstants } from '../Theme/mindly_constants';
|
|
3
|
+
|
|
4
|
+
interface ContainerProps {
|
|
5
|
+
isActive: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Container = styled.div<ContainerProps>`
|
|
9
|
+
padding: 16px 8px;
|
|
10
|
+
border-radius: 4px;
|
|
11
|
+
border: 1px solid;
|
|
12
|
+
width: 100%;
|
|
13
|
+
min-width: 86px;
|
|
14
|
+
max-height: 108px;
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
border-color: ${(props) =>
|
|
17
|
+
props.isActive ? colorConstants.primaryColor : colorConstants.StrokeGray};
|
|
18
|
+
background: ${(props) =>
|
|
19
|
+
props.isActive ? colorConstants.secondaryColor : colorConstants.White};
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
align-items: center;
|
|
23
|
+
box-shadow: 0 5px 8px rgba(0, 0, 0, 0.04);
|
|
24
|
+
|
|
25
|
+
h4 {
|
|
26
|
+
margin-bottom: 4px;
|
|
27
|
+
color: ${(props) =>
|
|
28
|
+
props.isActive
|
|
29
|
+
? colorConstants.primaryColor
|
|
30
|
+
: colorConstants.secondaryTextColor};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h1 {
|
|
34
|
+
color: ${(props) =>
|
|
35
|
+
props.isActive
|
|
36
|
+
? colorConstants.primaryColor
|
|
37
|
+
: colorConstants.primaryTextColor};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
h5 {
|
|
41
|
+
color: ${(props) =>
|
|
42
|
+
props.isActive
|
|
43
|
+
? colorConstants.primaryColor
|
|
44
|
+
: colorConstants.secondaryTextColor};
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const WeekDayH1 = styled.h1`
|
|
49
|
+
line-height: 76px;
|
|
50
|
+
font-weight: 700;
|
|
51
|
+
margin: 0;
|
|
52
|
+
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { DatePicker } from './DatePicker';
|
|
4
|
+
|
|
5
|
+
describe('Test date picker', () => {
|
|
6
|
+
test('Smoke test', () => {
|
|
7
|
+
render(<DatePicker day="monday" date="12" isActive month="jan" />);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Container, WeekDayH1 } from './DatePicker.style';
|
|
3
|
+
import '../Theme/global.css';
|
|
4
|
+
|
|
5
|
+
export interface DatePickerProps {
|
|
6
|
+
/*
|
|
7
|
+
* Button type
|
|
8
|
+
*/
|
|
9
|
+
type: 'date' | 'weekday';
|
|
10
|
+
/*
|
|
11
|
+
* Weekday. Example: "Пн"
|
|
12
|
+
*/
|
|
13
|
+
weekday?: string;
|
|
14
|
+
/*
|
|
15
|
+
* Abbreviated to 2 letters day of week
|
|
16
|
+
*/
|
|
17
|
+
day?: string;
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* Day of month
|
|
21
|
+
*/
|
|
22
|
+
date?: string;
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Abbreviated to 3 letters month name
|
|
26
|
+
*/
|
|
27
|
+
month?: string;
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Status defining condition (set isActive on click)
|
|
31
|
+
*/
|
|
32
|
+
isActive: boolean;
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* Default react onClick
|
|
36
|
+
*/
|
|
37
|
+
onClick: (props?: React.SyntheticEvent) => void;
|
|
38
|
+
['data-testid']?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const DatePicker: React.FC<DatePickerProps> = (props) => {
|
|
42
|
+
return (
|
|
43
|
+
<Container
|
|
44
|
+
data-testid={props['data-testid']}
|
|
45
|
+
isActive={props.isActive}
|
|
46
|
+
onClick={() => props.onClick()}
|
|
47
|
+
>
|
|
48
|
+
{props.type === 'weekday' ? (
|
|
49
|
+
<WeekDayH1>{props.weekday}</WeekDayH1>
|
|
50
|
+
) : (
|
|
51
|
+
<>
|
|
52
|
+
<h4>{props.day}</h4>
|
|
53
|
+
<h1 className="bold">{props.date}</h1>
|
|
54
|
+
<h5>{props.month}</h5>
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
</Container>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
interface ContainerProps {
|
|
4
|
+
bottomHeight: number;
|
|
5
|
+
rightWidth: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Container = styled.a<ContainerProps>`
|
|
9
|
+
display: block;
|
|
10
|
+
width: 54px;
|
|
11
|
+
height: 54px;
|
|
12
|
+
position: fixed;
|
|
13
|
+
bottom: ${(props) => props.bottomHeight}px;
|
|
14
|
+
right: ${(props) => props.rightWidth}px;
|
|
15
|
+
z-index: 10000000000000;
|
|
16
|
+
|
|
17
|
+
img {
|
|
18
|
+
background: none;
|
|
19
|
+
border-radius: 50%;
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FloatingButton } from './FloatingButton';
|
|
4
|
+
|
|
5
|
+
describe('Test floating button', () => {
|
|
6
|
+
test('Smoke test', () => {
|
|
7
|
+
render(<FloatingButton bottomHeight={20} rightWidth={20} />);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Container } from './FloatingButton.style';
|
|
3
|
+
import img from './floating button.svg';
|
|
4
|
+
import '../Theme/global.css';
|
|
5
|
+
|
|
6
|
+
export interface FloatingButtonType {
|
|
7
|
+
/*
|
|
8
|
+
* The position of the button relative to the bottom edge. Conveys a value in pixels
|
|
9
|
+
*/
|
|
10
|
+
bottomHeight: number;
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* The position of the button relative to the right edge. Conveys a value in pixels
|
|
14
|
+
*/
|
|
15
|
+
rightWidth: number;
|
|
16
|
+
['data-testid']?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const FloatingButton: React.FC<FloatingButtonType> = (props) => {
|
|
20
|
+
return (
|
|
21
|
+
<Container
|
|
22
|
+
href="https://t.me/MindlySupport"
|
|
23
|
+
bottomHeight={props.bottomHeight}
|
|
24
|
+
rightWidth={props.rightWidth}
|
|
25
|
+
>
|
|
26
|
+
<img data-testid={props['data-testid']} src={img} alt="support mindly" />
|
|
27
|
+
</Container>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="54" height="54" viewBox="0 0 54 54" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="54" height="54" fill="#21ABD2"/>
|
|
3
|
+
<path d="M27.0002 15C21.9405 15 17.8237 19.1168 17.8237 24.1765V32.647C17.8237 33.8148 18.7737 34.7648 19.9413 34.7648C21.1092 34.7648 22.0591 33.8148 22.0591 32.647V25.5883C22.0591 24.4206 21.1092 23.4706 19.9413 23.4706C19.7037 23.4706 19.4788 23.5188 19.2651 23.5913C19.5663 19.5829 22.9168 16.4117 27.0002 16.4117C31.0815 16.4117 34.431 19.58 34.735 23.5856C34.5226 23.5129 34.2964 23.4706 34.0591 23.4706C32.8913 23.4706 31.9413 24.4206 31.9413 25.5883V32.647C31.9413 33.8148 32.8913 34.7648 34.0591 34.7648C34.3078 34.7648 34.5431 34.7139 34.765 34.6348V35.4706C34.765 35.8601 34.4479 36.1765 34.0591 36.1765H28.9879C28.6954 35.3566 27.9192 34.7648 27.0002 34.7648C25.8326 34.7648 24.8826 35.7147 24.8826 36.8824C24.8826 38.05 25.8326 39 27.0002 39C27.9192 39 28.6954 38.408 28.9879 37.5883H34.0591C35.2268 37.5883 36.1768 36.6383 36.1768 35.4706C36.1768 31.2248 36.1768 28.4269 36.1768 24.1765C36.1768 19.1168 32.06 15 27.0002 15V15Z" fill="white"/>
|
|
4
|
+
<path d="M37.5884 25.0122V33.2227C38.4081 32.9304 39.0001 32.1542 39.0001 31.235V26.9998C39.0001 26.0808 38.4081 25.3046 37.5884 25.0122Z" fill="white"/>
|
|
5
|
+
<path d="M15 26.9998V31.235C15 32.1542 15.592 32.9304 16.4117 33.2227V25.0122C15.592 25.3046 15 26.0808 15 26.9998Z" fill="white"/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { IonIcon } from '@ionic/react';
|
|
3
|
+
import { colorConstants } from '../Theme/mindly_constants';
|
|
4
|
+
|
|
5
|
+
interface ContainerProps {
|
|
6
|
+
isIos: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Container = styled.div<ContainerProps>`
|
|
10
|
+
padding: ${(props) => (props.isIos ? '16px 24px 50px' : '16px 24px')};
|
|
11
|
+
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.12);
|
|
12
|
+
display: flex;
|
|
13
|
+
position: fixed;
|
|
14
|
+
z-index: 10000000000000;
|
|
15
|
+
background: ${colorConstants.White};
|
|
16
|
+
bottom: -1px;
|
|
17
|
+
justify-content: space-between;
|
|
18
|
+
align-items: center;
|
|
19
|
+
width: 100%;
|
|
20
|
+
|
|
21
|
+
@media (min-width: 600px) {
|
|
22
|
+
padding-left: calc((100vw - 468px) / 2);
|
|
23
|
+
padding-right: calc((100vw - 468px) / 2);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@media (min-width: 1024px) {
|
|
27
|
+
padding-left: calc((100vw - 960px) / 2);
|
|
28
|
+
padding-right: calc((100vw - 960px) / 2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
button {
|
|
32
|
+
min-height: 44px !important;
|
|
33
|
+
width: 132px;
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export const InfoContainer = styled.div`
|
|
38
|
+
h4 {
|
|
39
|
+
color: ${colorConstants.primaryTextColor};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
h4:first-child {
|
|
43
|
+
margin-bottom: 4px;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
+
export const SmallIcon = styled<any>(IonIcon)`
|
|
49
|
+
// #skipcq
|
|
50
|
+
margin: 0 5px -4px 0;
|
|
51
|
+
font-size: 18px;
|
|
52
|
+
|
|
53
|
+
&.ion-color {
|
|
54
|
+
--ion-color-base: ${colorConstants.primaryColor} !important;
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { FooterForBooking } from './FooterForBooking';
|
|
4
|
+
|
|
5
|
+
describe('Test floating button', () => {
|
|
6
|
+
test('Smoke test', () => {
|
|
7
|
+
render(
|
|
8
|
+
<FooterForBooking
|
|
9
|
+
eventHandler={() => undefined}
|
|
10
|
+
price="1250 грн"
|
|
11
|
+
duration={50}
|
|
12
|
+
isIos
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('Smoke test', () => {
|
|
18
|
+
const handleEvent = jest.fn();
|
|
19
|
+
render(
|
|
20
|
+
<FooterForBooking
|
|
21
|
+
eventHandler={handleEvent}
|
|
22
|
+
price="1250 грн"
|
|
23
|
+
duration={50}
|
|
24
|
+
isIos
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
screen.getByText('Записатися').click();
|
|
28
|
+
expect(handleEvent).toHaveBeenCalledTimes(1);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Container, InfoContainer, SmallIcon } from './FooterForBooking.style';
|
|
3
|
+
import { Button } from '../button/Button';
|
|
4
|
+
import { timeOutline, cardOutline } from 'ionicons/icons';
|
|
5
|
+
import '../Theme/global.css';
|
|
6
|
+
|
|
7
|
+
export interface FooterForBookingProps {
|
|
8
|
+
/*
|
|
9
|
+
* Function to switch on booking system
|
|
10
|
+
*/
|
|
11
|
+
eventHandler: (props?: React.SyntheticEvent) => void;
|
|
12
|
+
|
|
13
|
+
/*
|
|
14
|
+
* Duration of consultation (in mn)
|
|
15
|
+
*/
|
|
16
|
+
duration: number;
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* Consultation cost. You need to transfer it together with the type of currency
|
|
20
|
+
*/
|
|
21
|
+
price: string;
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* If platform === ios set this props true
|
|
25
|
+
*/
|
|
26
|
+
isIos: boolean;
|
|
27
|
+
['data-testid']?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const FooterForBooking: React.FC<FooterForBookingProps> = (props) => {
|
|
31
|
+
return (
|
|
32
|
+
<Container isIos={props.isIos}>
|
|
33
|
+
<InfoContainer>
|
|
34
|
+
<h4>
|
|
35
|
+
<SmallIcon icon={timeOutline} color="primary" />
|
|
36
|
+
<>{props.duration} хв</>
|
|
37
|
+
</h4>
|
|
38
|
+
<h4>
|
|
39
|
+
<SmallIcon icon={cardOutline} color="primary" />
|
|
40
|
+
{props.price}
|
|
41
|
+
</h4>
|
|
42
|
+
</InfoContainer>
|
|
43
|
+
<Button
|
|
44
|
+
buttonType={'primary'}
|
|
45
|
+
isDisabled={false}
|
|
46
|
+
data-testid={props['data-testid']}
|
|
47
|
+
onClick={props.eventHandler} // #skipcq
|
|
48
|
+
>
|
|
49
|
+
Записатися
|
|
50
|
+
</Button>
|
|
51
|
+
</Container>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { IonIcon, IonInput } from '@ionic/react';
|
|
2
|
+
import { colorConstants } from '../Theme/mindly_constants';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
export const InputIonIcon = styled<any>(IonIcon)`
|
|
7
|
+
// skipcq JS-0323
|
|
8
|
+
width: 20px;
|
|
9
|
+
z-index: 3;
|
|
10
|
+
height: 20px;
|
|
11
|
+
position: absolute;
|
|
12
|
+
top: calc(50% - 10px);
|
|
13
|
+
left: 20px;
|
|
14
|
+
color: ${colorConstants.primaryTextColor} !important;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
interface CustomIonInputProps {
|
|
18
|
+
isActive: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const CustomIonInput = styled(IonInput)<CustomIonInputProps>`
|
|
22
|
+
position: relative;
|
|
23
|
+
border: none;
|
|
24
|
+
border-radius: 4px;
|
|
25
|
+
font-size: 14px;
|
|
26
|
+
font-family: 'Lato Regular', sans-serif;
|
|
27
|
+
--padding-start: 55px !important;
|
|
28
|
+
--background: ${colorConstants.AccentInputBgColor} !important;
|
|
29
|
+
min-height: 36px;
|
|
30
|
+
line-height: 20px;
|
|
31
|
+
color: ${(props) =>
|
|
32
|
+
props.isActive
|
|
33
|
+
? colorConstants.primaryTextColor
|
|
34
|
+
: colorConstants.AccentDisabledColor};
|
|
35
|
+
--placeholder-color: ${colorConstants.AccentDisabledColor};
|
|
36
|
+
--placeholder-opacity: 1;
|
|
37
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Input } from './Input';
|
|
4
|
+
import { personOutline } from 'ionicons/icons';
|
|
5
|
+
|
|
6
|
+
describe('Test input', () => {
|
|
7
|
+
test('Smoke test', () => {
|
|
8
|
+
render(
|
|
9
|
+
<Input
|
|
10
|
+
data-cy="input"
|
|
11
|
+
icon={personOutline}
|
|
12
|
+
isActive
|
|
13
|
+
name={'input'}
|
|
14
|
+
onIonChange={() => undefined}
|
|
15
|
+
placeholder={'input'}
|
|
16
|
+
type="text"
|
|
17
|
+
value=""
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CustomIonInput, InputIonIcon } from './Input.style';
|
|
3
|
+
import '../Theme/global.css';
|
|
4
|
+
|
|
5
|
+
export interface InputProps {
|
|
6
|
+
/*
|
|
7
|
+
* Toggles the color of text in the input (value !== '')
|
|
8
|
+
*/
|
|
9
|
+
isActive: boolean;
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Default react input name
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* Default input type
|
|
18
|
+
*/
|
|
19
|
+
type:
|
|
20
|
+
| 'date'
|
|
21
|
+
| 'email'
|
|
22
|
+
| 'number'
|
|
23
|
+
| 'password'
|
|
24
|
+
| 'search'
|
|
25
|
+
| 'tel'
|
|
26
|
+
| 'text'
|
|
27
|
+
| 'url'
|
|
28
|
+
| 'time'
|
|
29
|
+
| 'week'
|
|
30
|
+
| 'month'
|
|
31
|
+
| 'datetime-local';
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Default input placeholder
|
|
35
|
+
*/
|
|
36
|
+
placeholder: string;
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* Default input value
|
|
40
|
+
*/
|
|
41
|
+
value: string;
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* Data test id for cypress
|
|
45
|
+
*/
|
|
46
|
+
['data-testid']?: string;
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* Default onIonChange
|
|
50
|
+
*/
|
|
51
|
+
onIonChange: (e: CustomEvent) => void;
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* Link on icon
|
|
55
|
+
*/
|
|
56
|
+
icon: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const Input: React.FC<InputProps> = (props) => {
|
|
60
|
+
return (
|
|
61
|
+
<CustomIonInput
|
|
62
|
+
name={props.name}
|
|
63
|
+
placeholder={props.placeholder}
|
|
64
|
+
value={props.value}
|
|
65
|
+
data-testid={props['data-testid']}
|
|
66
|
+
onIonChange={(e) => props.onIonChange(e)}
|
|
67
|
+
isActive={props.isActive}
|
|
68
|
+
type={props.type}
|
|
69
|
+
>
|
|
70
|
+
<InputIonIcon icon={props.icon} />
|
|
71
|
+
</CustomIonInput>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { colorConstants } from '../Theme/mindly_constants';
|
|
3
|
+
import { IonIcon } from '@ionic/react';
|
|
4
|
+
|
|
5
|
+
export const Container = styled.button`
|
|
6
|
+
display: flex;
|
|
7
|
+
justify-content: space-between;
|
|
8
|
+
color: ${colorConstants.primaryTextColor};
|
|
9
|
+
border: 0;
|
|
10
|
+
border-bottom: 1px solid ${colorConstants.AccentDivider};
|
|
11
|
+
width: 100%;
|
|
12
|
+
padding: 10px 16px;
|
|
13
|
+
background: inherit;
|
|
14
|
+
align-items: center;
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
export const CustomIonIcon = styled<any>(IonIcon)`
|
|
19
|
+
// #skipcq
|
|
20
|
+
font-size: 24px;
|
|
21
|
+
`;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ListButton } from './ListButton';
|
|
4
|
+
import { personOutline } from 'ionicons/icons';
|
|
5
|
+
|
|
6
|
+
describe('Test list button', () => {
|
|
7
|
+
test('Smoke test', () => {
|
|
8
|
+
render(
|
|
9
|
+
<ListButton onClick={() => false} icon={personOutline}>
|
|
10
|
+
Кнопка
|
|
11
|
+
</ListButton>
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('Smoke test', () => {
|
|
16
|
+
const handleEvent = jest.fn();
|
|
17
|
+
render(
|
|
18
|
+
<ListButton onClick={handleEvent} icon={personOutline}>
|
|
19
|
+
Кнопка
|
|
20
|
+
</ListButton>
|
|
21
|
+
);
|
|
22
|
+
const listButton = screen.getByText('Кнопка');
|
|
23
|
+
listButton.click();
|
|
24
|
+
expect(handleEvent).toHaveBeenCalledTimes(1);
|
|
25
|
+
});
|
|
26
|
+
});
|