mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 03:43:28 +10:00
Add Messages component
This commit is contained in:
parent
f88293da07
commit
b362e30054
9 changed files with 316 additions and 2 deletions
80
src/components/Messages/Messages.css
Normal file
80
src/components/Messages/Messages.css
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
@import '../../marketplace.css';
|
||||
|
||||
.root {
|
||||
margin: 0;
|
||||
|
||||
/* Clearfix */
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.messageItem {
|
||||
margin-bottom: 35px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message,
|
||||
.ownMessage {
|
||||
/* Preserve newlines, but collapse other whitespace */
|
||||
white-space: pre-line;
|
||||
max-width: 433px;
|
||||
}
|
||||
|
||||
.message {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.ownMessage {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex-shrink: 0;
|
||||
margin: 9px 12px 0 0;
|
||||
}
|
||||
|
||||
.messageContent,
|
||||
.ownMessageContent {
|
||||
@apply --marketplaceMessageFontStyles;
|
||||
|
||||
margin: 0;
|
||||
padding: 15px 14px 11px 14px;
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--boxShadow);
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.messageContent {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ownMessageContent {
|
||||
background-color: var(--marketplaceColor);
|
||||
color: var(--matterColorLight);
|
||||
}
|
||||
|
||||
.messageDate,
|
||||
.ownMessageDate {
|
||||
@apply --marketplaceMessageDateFontStyles;
|
||||
margin: 11px 0 0 0;
|
||||
color: var(--matterColorAnti);
|
||||
|
||||
@media (--viewportMedium) {
|
||||
margin: 14px 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ownMessageDate {
|
||||
text-align: right;
|
||||
}
|
||||
35
src/components/Messages/Messages.example.js
Normal file
35
src/components/Messages/Messages.example.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { createUser, createCurrentUser, createMessage } from '../../util/test-data';
|
||||
import Messages from './Messages';
|
||||
|
||||
export const Empty = {
|
||||
component: Messages,
|
||||
props: {
|
||||
messages: [],
|
||||
currentUser: null,
|
||||
},
|
||||
group: 'messages',
|
||||
};
|
||||
|
||||
export const WithoutCurrentUser = {
|
||||
component: Messages,
|
||||
props: {
|
||||
messages: [
|
||||
createMessage('msg1', {}, { sender: createUser('user1') }),
|
||||
createMessage('msg2', {}, { sender: createUser('user2') }),
|
||||
],
|
||||
currentUser: null,
|
||||
},
|
||||
group: 'messages',
|
||||
};
|
||||
|
||||
export const WithCurrentUser = {
|
||||
component: Messages,
|
||||
props: {
|
||||
messages: [
|
||||
createMessage('msg1', {}, { sender: createUser('user1') }),
|
||||
createMessage('msg2', {}, { sender: createUser('user2') }),
|
||||
],
|
||||
currentUser: createCurrentUser('user2'),
|
||||
},
|
||||
group: 'messages',
|
||||
};
|
||||
92
src/components/Messages/Messages.js
Normal file
92
src/components/Messages/Messages.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
import React from 'react';
|
||||
import { string, arrayOf } from 'prop-types';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { Avatar } from '../../components';
|
||||
import { formatDate } from '../../util/dates';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
import css from './Messages.css';
|
||||
|
||||
const Message = props => {
|
||||
const { message, intl } = props;
|
||||
const todayString = intl.formatMessage({ id: 'Messages.today' });
|
||||
return (
|
||||
<div className={css.message}>
|
||||
<Avatar className={css.avatar} user={message.sender} />
|
||||
<div>
|
||||
<p className={css.messageContent}>{message.attributes.content}</p>
|
||||
<p className={css.messageDate}>{formatDate(intl, todayString, message.attributes.at)}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Message.propTypes = {
|
||||
message: propTypes.message.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const OwnMessage = props => {
|
||||
const { message, intl } = props;
|
||||
const todayString = intl.formatMessage({ id: 'Messages.today' });
|
||||
return (
|
||||
<div className={css.ownMessage}>
|
||||
<p className={css.ownMessageContent}>{message.attributes.content}</p>
|
||||
<p className={css.ownMessageDate}>{formatDate(intl, todayString, message.attributes.at)}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
OwnMessage.propTypes = {
|
||||
message: propTypes.message.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const MessagesComponent = props => {
|
||||
const { rootClassName, className, messages, currentUser, intl } = props;
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
const msg = message => {
|
||||
const isOwnMessage =
|
||||
message.sender &&
|
||||
message.sender.id &&
|
||||
currentUser &&
|
||||
currentUser.id &&
|
||||
message.sender.id.uuid === currentUser.id.uuid;
|
||||
if (isOwnMessage) {
|
||||
return <OwnMessage message={message} intl={intl} />;
|
||||
}
|
||||
return <Message message={message} intl={intl} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<ul className={classes}>
|
||||
{messages.map(m => (
|
||||
<li key={m.id.uuid} className={css.messageItem}>
|
||||
{msg(m)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
MessagesComponent.defaultProps = {
|
||||
rootClassName: null,
|
||||
className: null,
|
||||
};
|
||||
|
||||
MessagesComponent.propTypes = {
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
|
||||
messages: arrayOf(propTypes.message),
|
||||
currentUser: propTypes.currentUser,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const Messages = injectIntl(MessagesComponent);
|
||||
|
||||
export default Messages;
|
||||
19
src/components/Messages/Messages.test.js
Normal file
19
src/components/Messages/Messages.test.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import { fakeIntl, createUser, createCurrentUser, createMessage } from '../../util/test-data';
|
||||
import { MessagesComponent } from './Messages';
|
||||
|
||||
describe('Messages', () => {
|
||||
it('matches snapshot', () => {
|
||||
const props = {
|
||||
messages: [
|
||||
createMessage('msg1', {}, { sender: createUser('user1') }),
|
||||
createMessage('msg2', {}, { sender: createUser('user2') }),
|
||||
],
|
||||
currentUser: createCurrentUser('user2'),
|
||||
intl: fakeIntl,
|
||||
};
|
||||
const tree = renderDeep(<MessagesComponent {...props} />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
63
src/components/Messages/__snapshots__/Messages.test.js.snap
Normal file
63
src/components/Messages/__snapshots__/Messages.test.js.snap
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Messages matches snapshot 1`] = `
|
||||
<ul
|
||||
className=""
|
||||
>
|
||||
<li
|
||||
className={undefined}
|
||||
>
|
||||
<div
|
||||
className={undefined}
|
||||
>
|
||||
<a
|
||||
className=""
|
||||
href="/u/user1"
|
||||
onClick={[Function]}
|
||||
style={Object {}}
|
||||
title="user1 display name"
|
||||
>
|
||||
<span
|
||||
className={undefined}
|
||||
>
|
||||
TT
|
||||
</span>
|
||||
</a>
|
||||
<div>
|
||||
<p
|
||||
className={undefined}
|
||||
>
|
||||
Message msg1
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</p>
|
||||
<p
|
||||
className={undefined}
|
||||
>
|
||||
2017-11-09, 08:12
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className={undefined}
|
||||
>
|
||||
<div
|
||||
className={undefined}
|
||||
>
|
||||
<p
|
||||
className={undefined}
|
||||
>
|
||||
Message msg2
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</p>
|
||||
<p
|
||||
className={undefined}
|
||||
>
|
||||
2017-11-09, 08:12
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
|
|
@ -49,10 +49,10 @@ export { default as ImageCarousel } from './ImageCarousel/ImageCarousel';
|
|||
export { default as ImageFromFile } from './ImageFromFile/ImageFromFile';
|
||||
export { default as LayoutSideNavigation } from './LayoutSideNavigation/LayoutSideNavigation';
|
||||
export { default as LayoutSingleColumn } from './LayoutSingleColumn/LayoutSingleColumn';
|
||||
export { default as LayoutWrapperTopbar } from './LayoutWrapperTopbar/LayoutWrapperTopbar';
|
||||
export { default as LayoutWrapperFooter } from './LayoutWrapperFooter/LayoutWrapperFooter';
|
||||
export { default as LayoutWrapperMain } from './LayoutWrapperMain/LayoutWrapperMain';
|
||||
export { default as LayoutWrapperSideNav } from './LayoutWrapperSideNav/LayoutWrapperSideNav';
|
||||
export { default as LayoutWrapperFooter } from './LayoutWrapperFooter/LayoutWrapperFooter';
|
||||
export { default as LayoutWrapperTopbar } from './LayoutWrapperTopbar/LayoutWrapperTopbar';
|
||||
export { default as ListingCard } from './ListingCard/ListingCard';
|
||||
export {
|
||||
default as LocationAutocompleteInput,
|
||||
|
|
@ -66,6 +66,7 @@ export { default as Menu } from './Menu/Menu';
|
|||
export { default as MenuContent } from './MenuContent/MenuContent';
|
||||
export { default as MenuItem } from './MenuItem/MenuItem';
|
||||
export { default as MenuLabel } from './MenuLabel/MenuLabel';
|
||||
export { default as Messages } from './Messages/Messages';
|
||||
export { default as Modal } from './Modal/Modal';
|
||||
export { default as ModalInMobile } from './ModalInMobile/ModalInMobile';
|
||||
export { default as NamedLink } from './NamedLink/NamedLink';
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import * as LocationAutocompleteInput from './components/LocationAutocompleteInp
|
|||
import * as ManageListingCard from './components/ManageListingCard/ManageListingCard.example';
|
||||
import * as Map from './components/Map/Map.example';
|
||||
import * as Menu from './components/Menu/Menu.example';
|
||||
import * as Messages from './components/Messages/Messages.example';
|
||||
import * as Modal from './components/Modal/Modal.example';
|
||||
import * as ModalInMobile from './components/ModalInMobile/ModalInMobile.example';
|
||||
import * as NamedLink from './components/NamedLink/NamedLink.example';
|
||||
|
|
@ -97,6 +98,7 @@ export {
|
|||
ManageListingCard,
|
||||
Map,
|
||||
Menu,
|
||||
Messages,
|
||||
Modal,
|
||||
ModalInMobile,
|
||||
NamedLink,
|
||||
|
|
|
|||
|
|
@ -210,6 +210,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
--marketplaceMessageFontStyles {
|
||||
font-family: 'sofiapro', Helvetica, Arial, sans-serif;
|
||||
font-weight: var(--fontWeightRegular);
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
--marketplaceMessageDateFontStyles {
|
||||
font-family: 'sofiapro', Helvetica, Arial, sans-serif;
|
||||
font-weight: var(--fontWeightRegular);
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
letter-spacing: -0.1px;
|
||||
|
||||
@media (--viewportMedium) {
|
||||
font-weight: var(--fontWeightMedium);
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ================ Tabbed navigation font styles ================ */
|
||||
|
||||
--marketplaceTabNavFontStyles {
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@
|
|||
"ManageListingsPage.youHaveListings": "You have {count} {count, plural, one {listing} other {listings}}",
|
||||
"ManageListingsPage.yourListings": "Your listings",
|
||||
"MapPriceMarker.unsupportedPrice": "({currency})",
|
||||
"Messages.today": "Today",
|
||||
"Modal.close": "CLOSE",
|
||||
"Modal.closeModal": "Close modal",
|
||||
"NotFoundPage.description": "We can't find the page or the sauna you're looking for. Make sure you've typed the URL correctly, of try searching Saunatime.",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue