Skip to content

Commit

Permalink
feat(reply): Integrate collaborators endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Mingze Xiao committed May 15, 2020
1 parent 56331b9 commit 0c40590
Show file tree
Hide file tree
Showing 20 changed files with 688 additions and 55 deletions.
2 changes: 1 addition & 1 deletion docs/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@
#preview-container {
width: 100vw;
height: 75vh;
}
}
5 changes: 5 additions & 0 deletions src/components/ItemList/ItemList.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.ba {
.ba-ItemList-row {
padding: 5px 30px 5px 15px;
}
}
44 changes: 44 additions & 0 deletions src/components/ItemList/ItemList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React, { SyntheticEvent } from 'react';
import noop from 'lodash/noop';
import ItemRow from './ItemRow';
import './ItemList.scss';

export type Props<T extends { id: string }> = {
activeItemIndex?: number;
itemRowAs?: JSX.Element;
items: T[];
onActivate?: (index: number) => void;
onSelect: (index: number, event: React.SyntheticEvent) => void;
};

const ItemList = <T extends { id: string }>({
activeItemIndex = 0,
itemRowAs = <ItemRow />,
items,
onActivate = noop,
onSelect,
...rest
}: Props<T>): JSX.Element => (
<ul data-testid="ba-ItemList" role="listbox" {...rest}>
{items.map((item, index) =>
React.cloneElement(itemRowAs, {
...item,
key: item.id,
className: 'ba-ItemList-row',
isActive: index === activeItemIndex,
onClick: (event: SyntheticEvent) => {
onSelect(index, event);
},
/* preventDefault on mousedown so blur doesn't happen before click */
onMouseDown: (event: SyntheticEvent) => {
event.preventDefault();
},
onMouseEnter: () => {
onActivate(index);
},
}),
)}
</ul>
);

export default ItemList;
11 changes: 11 additions & 0 deletions src/components/ItemList/ItemRow.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@import '~box-ui-elements/es/styles/variables';

.ba-ItemRow-name {
line-height: 15px;
}

.ba-ItemRow-email {
color: $bdl-gray-62;
font-size: 11px;
line-height: 15px;
}
31 changes: 31 additions & 0 deletions src/components/ItemList/ItemRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import DatalistItem from 'box-ui-elements/es/components/datalist-item';
import { UserMini, GroupMini } from '../../@types';
import './ItemRow.scss';

export type Props = {
id?: string;
item?: UserMini | GroupMini;
name?: string;
};

const ItemRow = ({ item, ...rest }: Props): JSX.Element | null => {
if (!item || !item.name) {
return null;
}

return (
<DatalistItem {...rest}>
<div className="ba-ItemRow-name" data-testid="ba-ItemRow-name">
{item.name}
</div>
{'email' in item && (
<div className="ba-ItemRow-email" data-testid="ba-ItemRow-email">
{item.email}
</div>
)}
</DatalistItem>
);
};

export default ItemRow;
53 changes: 53 additions & 0 deletions src/components/ItemList/__tests__/ItemList-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import ItemList, { Props } from '../ItemList';
import { Collaborator } from '../../../@types';

describe('components/ItemList', () => {
const defaults: Props<Collaborator> = {
items: [
{ id: 'testid1', name: 'test1' },
{ id: 'testid2', name: 'test2' },
],
onSelect: jest.fn(),
};

const getWrapper = (props = {}): ShallowWrapper => shallow(<ItemList {...defaults} {...props} />);

describe('render()', () => {
test('should render elements with correct props', () => {
const wrapper = getWrapper({ activeItemIndex: 1 });

const itemList = wrapper.find('[data-testid="ba-ItemList"]');
const firstChild = itemList.childAt(0);
const secondChild = itemList.childAt(1);

expect(itemList.props()).toMatchObject({
role: 'listbox',
});
expect(itemList.children()).toHaveLength(2);
expect(firstChild.prop('className')).toEqual('ba-ItemList-row');
expect(firstChild.prop('isActive')).toEqual(false);
expect(secondChild.prop('isActive')).toEqual(true);
});

test('should trigger events', () => {
const mockSetIndex = jest.fn();
const mockEvent = {
preventDefault: jest.fn(),
};

const wrapper = getWrapper({ onActivate: mockSetIndex });
const firstChild = wrapper.find('[data-testid="ba-ItemList"]').childAt(0);

firstChild.simulate('click', mockEvent);
expect(defaults.onSelect).toBeCalledWith(0, mockEvent);

firstChild.simulate('mousedown', mockEvent);
expect(mockEvent.preventDefault).toBeCalled();

firstChild.simulate('mouseenter');
expect(mockSetIndex).toBeCalledWith(0);
});
});
});
50 changes: 50 additions & 0 deletions src/components/ItemList/__tests__/ItemRow-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import DatalistItem from 'box-ui-elements/es/components/datalist-item';
import ItemRow, { Props } from '../ItemRow';

describe('components/Popups/ReplyField/ItemRow', () => {
const defaults: Props = {
id: 'testid',
item: { email: '[email protected]', id: 'testid', name: 'testname', type: 'user' },
name: 'testname',
};

const getWrapper = (props = {}): ShallowWrapper => shallow(<ItemRow {...defaults} {...props} />);

describe('render()', () => {
test('should render DatalistItem with correct props', () => {
const wrapper = getWrapper();

expect(wrapper.find(DatalistItem).props()).toMatchObject({
id: 'testid',
name: 'testname',
});
});

test('should not render anything if no item', () => {
const wrapper = getWrapper({ item: null });

expect(wrapper.exists(DatalistItem)).toBeFalsy();
});

test('should not render anything if no item name', () => {
const wrapper = getWrapper({ item: {} });

expect(wrapper.exists(DatalistItem)).toBeFalsy();
});

test('should render item name and email', () => {
const wrapper = getWrapper();

expect(wrapper.find('[data-testid="ba-ItemRow-name"]').text()).toBe('testname');
expect(wrapper.find('[data-testid="ba-ItemRow-email"]').text()).toBe('[email protected]');
});

test('should not render email if item has no email', () => {
const wrapper = getWrapper({ item: { id: 'testid', name: 'testname', type: 'group' } });

expect(wrapper.exists('[data-testid="ba-ItemRow-email"]')).toBeFalsy();
});
});
});
1 change: 1 addition & 0 deletions src/components/ItemList/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ItemList';
1 change: 1 addition & 0 deletions src/components/Popups/Popper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import unionBy from 'lodash/unionBy';

export type Instance = popper.Instance;
export type Options = popper.Options;
export type State = popper.State;
export type VirtualElement = popper.VirtualElement;

export type PopupReference = Element | VirtualElement;
Expand Down
6 changes: 6 additions & 0 deletions src/components/Popups/ReplyField/MentionItem.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import '~box-ui-elements/es/styles/variables';

.ba-MentionItem-link {
color: $bdl-box-blue;
font-weight: bold;
}
25 changes: 25 additions & 0 deletions src/components/Popups/ReplyField/MentionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';
import { ContentState } from 'draft-js';
import './MentionItem.scss';

export type Props = {
children: React.ReactNode;
contentState: ContentState;
entityKey: string;
};

const MentionItem = ({ contentState, entityKey, children }: Props): JSX.Element => {
const { id } = contentState.getEntity(entityKey).getData();

return id ? (
<a className="ba-MentionItem-link" data-testid="ba-MentionItem-link" href={`/profile/${id}`}>
{children}
</a>
) : (
<span className="ba-MentionItem-link" data-testid="ba-MentionItem-text">
{children}
</span>
);
};

export default MentionItem;
10 changes: 4 additions & 6 deletions src/components/Popups/ReplyField/PopupList.scss
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
@import '~box-ui-elements/es/styles/variables';

.ba-PopupList {
font-size: 13px;
@include common-typography;

.ba-Popup-arrow {
display: none;
}

.ba-Popup-content {
padding-top: 10px;
padding-bottom: 10px;
border: solid 1px $bdl-gray-30;
border-radius: $bdl-border-radius-size;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05);
}

.ba-PopupList-item {
padding: 10px;
}

.ba-PopupList-prompt {
padding-top: 0;
padding-right: 10px;
padding-left: 10px;
}
}
27 changes: 19 additions & 8 deletions src/components/Popups/ReplyField/PopupList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import * as React from 'react';
import React from 'react';
import isEmpty from 'lodash/isEmpty';
import { FormattedMessage } from 'react-intl';
import ItemList from '../../ItemList/ItemList';
import messages from '../messages';
import PopupBase from '../PopupBase';
import { Options, PopupReference } from '../Popper';

import './PopupList.scss';

export type Props = {
export type Props<T extends { id: string }> = {
activeItemIndex?: number;
itemRowAs?: JSX.Element;
items: T[];
onActivate?: (index: number) => void;
onSelect: (index: number, event: React.SyntheticEvent) => void;
options?: Partial<Options>;
reference: PopupReference;
};

Expand All @@ -28,11 +35,15 @@ const options: Partial<Options> = {
placement: 'bottom-start',
};

const PopupList = ({ reference, ...rest }: Props): JSX.Element => (
<PopupBase className="ba-PopupList" options={options} reference={reference} {...rest}>
<div className="ba-PopupList-prompt ba-PopupList-item">
<FormattedMessage {...messages.popupListPrompt} />
</div>
const PopupList = <T extends { id: string }>({ items, reference, ...rest }: Props<T>): JSX.Element => (
<PopupBase className="ba-PopupList" options={options} reference={reference}>
{isEmpty(items) ? (
<div className="ba-PopupList-prompt">
<FormattedMessage {...messages.popupListPrompt} />
</div>
) : (
<ItemList items={items} {...rest} />
)}
</PopupBase>
);

Expand Down
Loading

0 comments on commit 0c40590

Please sign in to comment.