Skip to content

Commit

Permalink
Marketplace shop switcher (#2412)
Browse files Browse the repository at this point in the history
* Remove unused media and tag db lookups

* Give all users access to shop selector.

* Modifies Shops publication to return all shops with matching domain by removing limit
* Rewrite of shop selector dropdown
* Use Shops publication instead of SellerShops publication in dropdown
* Remove requirement for marketplace ownership to see shop selector
* Change Reaction.ShopId when shop selector is changed
* TODO: Convert shop selector to React

* Set ShopId on shop selector change

* Regular Shop Selector should just adjust only the route.

* Remove empty line at top of navbar

* Change Reaction.shopId to be a ReactiveVar

* Add closeOnClick option to DropDownMenu

* Add shop selector to admin toolbar.
  • Loading branch information
spencern authored and Aaron Judd committed Jun 12, 2017
1 parent f024b86 commit e2d9335
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 68 deletions.
16 changes: 15 additions & 1 deletion client/modules/core/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const reactionState = new ReactiveDict();
* Global reaction shop permissions methods and shop initialization
*/
export default {
shopId: null,
_shopId: new ReactiveVar(null),

Locale: new ReactiveVar({}),

Expand Down Expand Up @@ -245,10 +245,24 @@ export default {
});
},

get shopId() {
return this._shopId.get();
},

getShopId() {
return this.shopId;
},

set shopId(id) {
this._shopId.set(id);
},

setShopId(id) {
if (id) {
this.shopId = id;
}
},

getShopName() {
return this.shopName;
},
Expand Down
39 changes: 39 additions & 0 deletions imports/plugins/core/dashboard/client/components/toolbar.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { Component, PropTypes } from "react";
import Blaze from "meteor/gadicc:blaze-react-component";
import {
DropDownMenu,
MenuItem,
FlatButton,
Toolbar,
ToolbarGroup,
Expand All @@ -20,9 +22,12 @@ class PublishControls extends Component {
isEnabled: PropTypes.bool,
isPreview: PropTypes.bool,
onAddProduct: PropTypes.func,
onShopSelectChange: PropTypes.func,
onViewContextChange: PropTypes.func,
onVisibilityChange: PropTypes.func,
packageButtons: PropTypes.arrayOf(PropTypes.object),
shopId: PropTypes.string,
shops: PropTypes.arrayOf(PropTypes.object),
showViewAsControls: PropTypes.bool,
translation: PropTypes.shape({
lang: PropTypes.string
Expand All @@ -39,6 +44,13 @@ class PublishControls extends Component {
}
}

// Passthrough to shopSelectChange handler in container above
onShopSelectChange = (event, shopId) => {
if (typeof this.props.onShopSelectChange === "function") {
this.props.onShopSelectChange(event, shopId);
}
}

renderViewControls() {
if (this.props.showViewAsControls) {
return (
Expand All @@ -61,6 +73,32 @@ class PublishControls extends Component {
return null;
}

renderShopSelect() {
let menuItems;
if (Array.isArray(this.props.shops)) {
menuItems = this.props.shops.map((shop, index) => {
return (
<MenuItem
label={shop.name}
selectLabel={shop.name}
value={shop._id}
key={index}
/>
);
});
}

return (
<DropDownMenu
onChange={this.onShopSelectChange}
value={this.props.shopId}
closeOnClick={true}
>
{menuItems}
</DropDownMenu>
);
}

renderVisibilitySwitch() {
if (this.props.hasCreateProductAccess) {
return (
Expand Down Expand Up @@ -146,6 +184,7 @@ class PublishControls extends Component {
<Toolbar>
<ToolbarGroup firstChild={true}>
{this.renderVisibilitySwitch()}
{this.renderShopSelect()}
</ToolbarGroup>
<ToolbarGroup lastChild={true}>
{this.renderAddButton()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from "react";
import { Meteor } from "meteor/meteor";
import { composeWithTracker } from "/lib/api/compose";
import { Reaction, i18next } from "/client/api";
import { Tags } from "/lib/collections";
import { Tags, Shops } from "/lib/collections";
import { TranslationProvider, AdminContextProvider } from "/imports/plugins/core/ui/client/providers";
import { isRevisionControlEnabled } from "/imports/plugins/core/revisions/lib/api";

Expand Down Expand Up @@ -50,9 +50,28 @@ const handleViewContextChange = (event, value) => {
}
};

/**
* Handler that fires when the shop selector is changed
* @param {Object} event - the `event` coming from the select change event
* @param {String} shopId - The `value` coming from the select change event
* @returns {undefined}
*/
const handleShopSelectChange = (event, shopId) => {
if (/^[A-Za-z0-9]{17}$/.test(shopId)) { // Make sure shopId is a valid ID
Reaction.setShopId(shopId);
}
};

function composer(props, onData) {
// Reactive data sources
const routeName = Reaction.Router.getRouteName();
const user = Meteor.user();
let shops;

if (user && user.roles) {
// Get all shops for which user has roles
shops = Shops.find({ _id: { $in: Object.keys(user.roles) } }).fetch();
}

// Standard variables
const packageButtons = [];
Expand Down Expand Up @@ -88,10 +107,13 @@ function composer(props, onData) {
isEnabled: isRevisionControlEnabled(),
isActionViewAtRootView: Reaction.isActionViewAtRootView(),
actionViewIsOpen: Reaction.isActionViewOpen(),
hasCreateProductAccess: Reaction.hasPermission("createProduct", Meteor.userId(), Reaction.getSellerShopId()),
hasCreateProductAccess: Reaction.hasPermission("createProduct", Meteor.userId(), Reaction.getShopId()),
shopId: Reaction.getShopId(),
shops: shops,

// Callbacks
onAddProduct: handleAddProduct,
onShopSelectChange: handleShopSelectChange,
onViewContextChange: handleViewContextChange
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@

<template name="CoreNavigationBar">
<div class="rui navbar">
<div class="showmenu">{{> button icon="bars" onClick=onMenuButtonClick}}</div>
{{> coreNavigationBrand}}
{{> shopSelect}}

{{#if isMarketplaceOwner}}
{{> shopSelect}}
<span>{{> React component=VerticalDivider}}</span>
{{/if}}
<span>
{{> React component=VerticalDivider}}
</span>

<div class="menu">
{{> tagNav tagNavProps}}
Expand Down
40 changes: 39 additions & 1 deletion imports/plugins/core/ui/client/components/menu/dropDownMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,53 @@ class DropDownMenu extends Component {
super(props);

this.state = {
label: undefined
label: undefined,
isOpen: false
};
}

componentWillReceiveProps(nextProps) {
if (this.isControlled) {
this.setState({
isOpen: nextProps.isOpen
});
}
}

get isOpen() {
return this.props.isOpen || this.state.isOpen;
}

get isControlled() {
return typeof this.props.isOpen === "boolean";
}

handleMenuItemChange = (event, value, menuItem) => {
this.setState({
label: menuItem.props.label || value
});

if (this.props.closeOnClick) {
this.handleOpen(false);
}

if (this.props.onChange) {
this.props.onChange(event, value);
}
}

handleOpen = (isOpen) => {
if (this.isControlled) {
if (this.props.onRequestOpen) {
this.props.onRequestOpen(isOpen);
}
} else {
this.setState({
isOpen: isOpen
});
}
}

get label() {
let label = this.state.label;
Children.forEach(this.props.children, (element) => {
Expand Down Expand Up @@ -53,6 +86,8 @@ class DropDownMenu extends Component {
label={this.label}
/>
}
isOpen={this.isOpen}
onRequestOpen={this.handleOpen}
>
<Menu value={this.props.value} onChange={this.handleMenuItemChange}>
{this.props.children}
Expand All @@ -65,9 +100,12 @@ class DropDownMenu extends Component {
DropDownMenu.propTypes = {
buttonElement: PropTypes.node,
children: PropTypes.node,
closeOnClick: PropTypes.bool,
isEnabled: PropTypes.bool,
isOpen: PropTypes.bool,
onChange: PropTypes.func,
onPublishClick: PropTypes.func,
onRequestOpen: PropTypes.func,
revisions: PropTypes.arrayOf(PropTypes.object),
translation: PropTypes.shape({
lang: PropTypes.string
Expand Down
44 changes: 37 additions & 7 deletions imports/plugins/core/ui/client/components/popover/popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ class Popover extends Component {
isOpen: false
}

componentWillReceiveProps(nextProps) {
if (this.isControlled) {
this.setState({
isOpen: nextProps.isOpen
});
}
}

get isOpen() {
return this.props.isOpen || this.state.isOpen;
}

get isControlled() {
return typeof this.props.isOpen === "boolean";
}

/**
* attachment
* @description Return the attachment for the tooltip or the default
Expand All @@ -25,19 +41,31 @@ class Popover extends Component {
}

handleOpen = () => {
this.setState({
isOpen: true
});
if (this.isControlled) {
if (this.props.onRequestOpen) {
this.props.onRequestOpen(true);
}
} else {
this.setState({
isOpen: true
});
}
}

handleClickOutside = () => {
this.setState({
isOpen: false
});
if (this.isControlled) {
if (this.props.onRequestOpen) {
this.props.onRequestOpen(false);
}
} else {
this.setState({
isOpen: false
});
}
}

renderPopoverChildren() {
if (this.state.isOpen) {
if (this.isOpen) {
return (
<PopoverContent
children={this.props.children}
Expand Down Expand Up @@ -96,7 +124,9 @@ Popover.propTypes = {
attachment: PropTypes.string,
buttonElement: PropTypes.node,
children: PropTypes.node,
isOpen: PropTypes.bool,
onDisplayButtonClick: PropTypes.func,
onRequestOpen: PropTypes.func,
showArrow: PropTypes.bool,
showDropdownButton: PropTypes.bool,
targetAttachment: PropTypes.string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template name="shopSelect">
{{#if sellerShops}}
{{#if shops}}
<div class="shops">
<div class="dropdown" role="shops">
<div class="dropdown-toggle"
Expand All @@ -8,18 +8,13 @@
aria-haspopup="true"
aria-expanded="false">

{{#if isChildShop}}
{{currentShopName}}
{{else if isOwnerShop}}
<span data-i18n="app.myShop">My Shop</span>
{{else}}
<span data-i18n="app.shops">Shops</span>
{{/if}}
<span>{{currentShopName}}</span>

<span class="caret"></span>
</div>
<ul class="dropdown-menu" role="menu">
{{#each sellerShops}}
<li class="{{class}}">
{{#each shops}}
<li class="{{isActiveShop _id}}">
<a class="shop" role="menuitem">{{name}}</a>
</li>
{{/each}}
Expand Down
Loading

0 comments on commit e2d9335

Please sign in to comment.