Bug 1212357 - Update the layout of the rooms list items for user journey. r=mikedeboer

This commit is contained in:
Manuel Casas 2015-10-28 09:42:20 -07:00
Родитель f4b96d2bbe
Коммит 6ecd092dbf
5 изменённых файлов: 96 добавлений и 135 удалений

Просмотреть файл

@ -186,12 +186,11 @@ body {
}
.rooms > h1 {
font-weight: bold;
color: #666;
font-size: 1rem;
padding: .5rem 0;
height: 3rem;
line-height: 3rem;
font-size: 1.1rem;
margin: 0 15px;
}
@ -205,7 +204,7 @@ body {
border-radius: 5px;
font-size: 1.2rem;
font-weight: bold;
margin: 1rem;
margin: 1.5rem;
padding: 1rem;
}
@ -252,8 +251,8 @@ body {
line-height: 2.4rem;
color: #000;
/* See .room-entry-context-item for the margin/size reductions.
* An extra 40px to make space for the call button and chevron. */
width: calc(100% - 1rem - 56px);
* An extra 16px to make space for the edit button. */
width: calc(100% - 1rem - 32px);
}
.room-list > .room-entry.room-active:not(.room-opened) > h2 {
@ -331,27 +330,9 @@ body {
vertical-align: middle;
}
.room-list > .room-entry > h2:before {
content: "";
display: inline-block;
background-image: url("../shared/img/icons-14x14.svg#hello");
background-repeat: no-repeat;
background-size: cover;
width: 13px;
height: 13px;
-moz-margin-end: 1rem;
margin-bottom: -3px;
}
.room-list > .room-entry.room-active > h2:before {
background-image: url("../shared/img/icons-14x14.svg#hello-active");
}
/* Room entry context button (call button + chevron) */
/* Room entry context button (edit button) */
.room-entry-context-actions {
display: none;
border-radius: 30px;
background: #56b397;
vertical-align: top;
}
@ -359,41 +340,17 @@ body {
display: inline-block;
}
.room-entry:hover .room-entry-context-actions:hover {
background: #50e3c2;
}
/* Room entry call button */
.room-entry-call-btn {
border-top-left-radius: 30px;
border-bottom-left-radius: 30px;
background-color: transparent;
background-image: url("../shared/img/icons-14x14.svg#video-white");
background-position: right center;
}
html[dir="rtl"] .room-entry-call-btn {
background-position: left center;
}
/* Room entry context menu */
.room-entry-context-menu-chevron {
display: inline-block;
border-top-right-radius: 30px;
border-bottom-right-radius: 30px;
background-image: url("../shared/img/icons-10x10.svg#dropdown-white");
/* Room entry edit button */
.room-entry-context-edit-btn {
background-image: url("../shared/img/icons-10x10.svg#edit-darkgrey");
background-position: center;
cursor: pointer;
}
/* Common styles for chevron and call button. */
.room-entry-context-menu-chevron,
.room-entry-call-btn {
width: 30px;
height: 24px;
background-size: 12px;
background-repeat: no-repeat;
background-size: 12px;
cursor: pointer;
display: inline-block;
height: 24px;
vertical-align: middle;
width: 16px;
}
html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
@ -410,16 +367,11 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
/* Keep ".room-list > .room-entry > h2" in sync with these. */
.room-entry-context-item {
display: inline-block;
-moz-margin-end: 1rem;
vertical-align: middle;
-moz-margin-start: 1rem;
height: 16px;
}
.room-entry:not(.room-opened):hover .room-entry-context-item {
display: none;
}
.room-entry-context-item > a > img {
.room-entry-context-item > img {
height: 16px;
width: 16px;
}

Просмотреть файл

@ -340,23 +340,39 @@ loop.panel = (function(_, mozL10n) {
handleClick: function(event) {
event.stopPropagation();
event.preventDefault();
this.props.mozLoop.openURL(event.currentTarget.href);
this.closeWindow();
if (event.currentTarget.href) {
this.props.mozLoop.openURL(event.currentTarget.href);
this.closeWindow();
}
},
render: function() {
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
if (!roomUrl) {
return null;
}
_renderDefaultIcon: function() {
return (
React.createElement("div", {className: "room-entry-context-item"},
React.createElement("a", {href: roomUrl.location, onClick: this.handleClick, title: roomUrl.description},
React.createElement("img", {src: "loop/shared/img/icons-16x16.svg#globe"})
)
);
},
_renderIcon: function(roomUrl) {
return (
React.createElement("div", {className: "room-entry-context-item"},
React.createElement("a", {href: roomUrl.location,
onClick: this.handleClick,
title: roomUrl.description},
React.createElement("img", {src: roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"})
)
)
);
},
render: function() {
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
if (roomUrl && roomUrl.location) {
return this._renderIcon(roomUrl);
} else {
return this._renderDefaultIcon();
}
}
});
@ -398,7 +414,7 @@ loop.panel = (function(_, mozL10n) {
this.closeWindow();
},
handleContextChevronClick: function(e) {
handleClick: function(e) {
e.preventDefault();
e.stopPropagation();
@ -435,23 +451,19 @@ loop.panel = (function(_, mozL10n) {
onClick: this.props.isOpenedRoom ? null : this.handleClickEntry,
onMouseLeave: this.props.isOpenedRoom ? null : this._handleMouseOut,
ref: "roomEntry"},
React.createElement("h2", null,
roomTitle
),
React.createElement(RoomEntryContextItem, {
mozLoop: this.props.mozLoop,
roomUrls: this.props.room.decryptedContext.urls}),
React.createElement("h2", null, roomTitle),
this.props.isOpenedRoom ? null :
React.createElement(RoomEntryContextButtons, {
dispatcher: this.props.dispatcher,
eventPosY: this.state.eventPosY,
handleClickEntry: this.handleClickEntry,
handleContextChevronClick: this.handleContextChevronClick,
handleClick: this.handleClick,
ref: "contextActions",
room: this.props.room,
showMenu: this.state.showMenu,
toggleDropdownMenu: this.toggleDropdownMenu})
)
);
}
@ -459,16 +471,14 @@ loop.panel = (function(_, mozL10n) {
/**
* Buttons corresponding to each conversation entry.
* This component renders the video icon call button and chevron button for
* displaying contextual dropdown menu for conversation entries.
* It also holds the dropdown menu.
* This component renders the edit button for displaying contextual dropdown
* menu for conversation entries. It also holds the dropdown menu.
*/
var RoomEntryContextButtons = React.createClass({displayName: "RoomEntryContextButtons",
propTypes: {
dispatcher: React.PropTypes.object.isRequired,
eventPosY: React.PropTypes.number.isRequired,
handleClickEntry: React.PropTypes.func.isRequired,
handleContextChevronClick: React.PropTypes.func.isRequired,
handleClick: React.PropTypes.func.isRequired,
room: React.PropTypes.object.isRequired,
showMenu: React.PropTypes.bool.isRequired,
toggleDropdownMenu: React.PropTypes.func.isRequired
@ -514,13 +524,9 @@ loop.panel = (function(_, mozL10n) {
render: function() {
return (
React.createElement("div", {className: "room-entry-context-actions"},
React.createElement("button", {
className: "btn room-entry-call-btn",
onClick: this.props.handleClickEntry,
ref: "callButton"}),
React.createElement("div", {
className: "room-entry-context-menu-chevron dropdown-menu-button",
onClick: this.props.handleContextChevronClick,
className: "room-entry-context-edit-btn dropdown-menu-button",
onClick: this.props.handleClick,
ref: "menu-button"}),
this.props.showMenu ?
React.createElement(ConversationDropdown, {
@ -563,7 +569,7 @@ loop.panel = (function(_, mozL10n) {
// Get the parent element and make sure the menu does not overlow its
// container.
var listNode = loop.shared.utils.findParentNode(this.getDOMNode(),
".rooms");
"rooms");
var listNodeRect = listNode.getBoundingClientRect();
// Click offset to not display the menu right next to the area clicked.

Просмотреть файл

@ -340,23 +340,39 @@ loop.panel = (function(_, mozL10n) {
handleClick: function(event) {
event.stopPropagation();
event.preventDefault();
this.props.mozLoop.openURL(event.currentTarget.href);
this.closeWindow();
if (event.currentTarget.href) {
this.props.mozLoop.openURL(event.currentTarget.href);
this.closeWindow();
}
},
render: function() {
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
if (!roomUrl) {
return null;
}
_renderDefaultIcon: function() {
return (
<div className="room-entry-context-item">
<a href={roomUrl.location} onClick={this.handleClick} title={roomUrl.description}>
<img src="loop/shared/img/icons-16x16.svg#globe" />
</div>
);
},
_renderIcon: function(roomUrl) {
return (
<div className="room-entry-context-item">
<a href={roomUrl.location}
onClick={this.handleClick}
title={roomUrl.description}>
<img src={roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"} />
</a>
</div>
);
},
render: function() {
var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
if (roomUrl && roomUrl.location) {
return this._renderIcon(roomUrl);
} else {
return this._renderDefaultIcon();
}
}
});
@ -398,7 +414,7 @@ loop.panel = (function(_, mozL10n) {
this.closeWindow();
},
handleContextChevronClick: function(e) {
handleClick: function(e) {
e.preventDefault();
e.stopPropagation();
@ -435,23 +451,19 @@ loop.panel = (function(_, mozL10n) {
onClick={this.props.isOpenedRoom ? null : this.handleClickEntry}
onMouseLeave={this.props.isOpenedRoom ? null : this._handleMouseOut}
ref="roomEntry">
<h2>
{roomTitle}
</h2>
<RoomEntryContextItem
mozLoop={this.props.mozLoop}
roomUrls={this.props.room.decryptedContext.urls} />
<h2>{roomTitle}</h2>
{this.props.isOpenedRoom ? null :
<RoomEntryContextButtons
dispatcher={this.props.dispatcher}
eventPosY={this.state.eventPosY}
handleClickEntry={this.handleClickEntry}
handleContextChevronClick={this.handleContextChevronClick}
handleClick={this.handleClick}
ref="contextActions"
room={this.props.room}
showMenu={this.state.showMenu}
toggleDropdownMenu={this.toggleDropdownMenu} />
}
toggleDropdownMenu={this.toggleDropdownMenu} />}
</div>
);
}
@ -459,16 +471,14 @@ loop.panel = (function(_, mozL10n) {
/**
* Buttons corresponding to each conversation entry.
* This component renders the video icon call button and chevron button for
* displaying contextual dropdown menu for conversation entries.
* It also holds the dropdown menu.
* This component renders the edit button for displaying contextual dropdown
* menu for conversation entries. It also holds the dropdown menu.
*/
var RoomEntryContextButtons = React.createClass({
propTypes: {
dispatcher: React.PropTypes.object.isRequired,
eventPosY: React.PropTypes.number.isRequired,
handleClickEntry: React.PropTypes.func.isRequired,
handleContextChevronClick: React.PropTypes.func.isRequired,
handleClick: React.PropTypes.func.isRequired,
room: React.PropTypes.object.isRequired,
showMenu: React.PropTypes.bool.isRequired,
toggleDropdownMenu: React.PropTypes.func.isRequired
@ -514,13 +524,9 @@ loop.panel = (function(_, mozL10n) {
render: function() {
return (
<div className="room-entry-context-actions">
<button
className="btn room-entry-call-btn"
onClick={this.props.handleClickEntry}
ref="callButton" />
<div
className="room-entry-context-menu-chevron dropdown-menu-button"
onClick={this.props.handleContextChevronClick}
className="room-entry-context-edit-btn dropdown-menu-button"
onClick={this.props.handleClick}
ref="menu-button" />
{this.props.showMenu ?
<ConversationDropdown
@ -563,7 +569,7 @@ loop.panel = (function(_, mozL10n) {
// Get the parent element and make sure the menu does not overlow its
// container.
var listNode = loop.shared.utils.findParentNode(this.getDOMNode(),
".rooms");
"rooms");
var listNodeRect = listNode.getBoundingClientRect();
// Click offset to not display the menu right next to the area clicked.

Просмотреть файл

@ -22,6 +22,9 @@
use[id$="-disabled"] {
fill: rgba(255,255,255,0.4);
}
use[id$="-darkgrey"] {
fill: #565656;
}
</style>
<defs>
<polygon id="close-shape" points="10,1.717 8.336,0.049 5.024,3.369 1.663,0 0,1.668 3.36,5.037 0.098,8.307 1.762,9.975 5.025,6.705 8.311,10 9.975,8.332 6.688,5.037"/>
@ -43,6 +46,7 @@
<use id="edit-active" xlink:href="#edit-shape"/>
<use id="edit-disabled" xlink:href="#edit-shape"/>
<use id="edit-white" xlink:href="#edit-shape"/>
<use id="edit-darkgrey" xlink:href="#edit-shape"/>
<use id="expand" xlink:href="#expand-shape"/>
<use id="expand-active" xlink:href="#expand-shape"/>
<use id="expand-disabled" xlink:href="#expand-shape"/>

До

Ширина:  |  Высота:  |  Размер: 5.9 KiB

После

Ширина:  |  Высота:  |  Размер: 6.0 KiB

Просмотреть файл

@ -593,7 +593,7 @@ describe("loop.panel", function() {
React.createElement(loop.panel.RoomEntry, props));
}
describe("handleContextChevronClick", function() {
describe("handleClick", function() {
var view;
beforeEach(function() {
@ -613,15 +613,15 @@ describe("loop.panel", function() {
expect(view.refs.contextActions.state.showMenu).to.eql(false);
});
it("should set eventPosY when handleContextChevronClick is called", function() {
view.handleContextChevronClick(fakeEvent);
it("should set eventPosY when handleClick is called", function() {
view.handleClick(fakeEvent);
expect(view.state.eventPosY).to.eql(fakeEvent.pageY);
});
it("toggle state.showMenu when handleContextChevronClick is called", function() {
it("toggle state.showMenu when handleClick is called", function() {
var prevState = view.state.showMenu;
view.handleContextChevronClick(fakeEvent);
view.handleClick(fakeEvent);
expect(view.state.showMenu).to.eql(!prevState);
});
@ -702,10 +702,10 @@ describe("loop.panel", function() {
});
}
it("should not display a context indicator if the room doesn't have any", function() {
it("should display a default context indicator if the room doesn't have any", function() {
roomEntry = mountEntryForContext();
expect(roomEntry.getDOMNode().querySelector(".room-entry-context-item")).eql(null);
expect(roomEntry.getDOMNode().querySelector(".room-entry-context-item")).not.eql(null);
});
it("should a context indicator if the room specifies context", function() {
@ -1129,11 +1129,10 @@ describe("loop.panel", function() {
var props = _.extend({
dispatcher: dispatcher,
eventPosY: 0,
handleClickEntry: sandbox.stub(),
showMenu: false,
room: roomData,
toggleDropdownMenu: sandbox.stub(),
handleContextChevronClick: sandbox.stub()
handleClick: sandbox.stub()
}, extraProps);
return TestUtils.renderIntoDocument(
React.createElement(loop.panel.RoomEntryContextButtons, props));
@ -1192,11 +1191,5 @@ describe("loop.panel", function() {
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.DeleteRoom({ roomToken: roomData.roomToken }));
});
it("should trigger handleClickEntry when button is clicked", function() {
TestUtils.Simulate.click(view.refs.callButton.getDOMNode());
sinon.assert.calledOnce(view.props.handleClickEntry);
});
});
});