From 0b0c4a4c97fb65cea800f9462585c9ba3c166402 Mon Sep 17 00:00:00 2001 From: gopienkonikita Date: Thu, 1 Aug 2019 17:24:39 +0300 Subject: [PATCH 1/4] Web: Components: rename "checkboxItem" in toggle-button component --- web/ASC.Web.Components/src/components/toggle-button/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/ASC.Web.Components/src/components/toggle-button/index.js b/web/ASC.Web.Components/src/components/toggle-button/index.js index c87f216be2..b89b16128b 100644 --- a/web/ASC.Web.Components/src/components/toggle-button/index.js +++ b/web/ASC.Web.Components/src/components/toggle-button/index.js @@ -31,7 +31,7 @@ const HiddenInput = styled.input` z-index: -1; `; -const CheckboxIcon = ({ isChecked }) => { +const ToggleIcon = ({ isChecked }) => { const iconName = isChecked ? "ToggleButtonCheckedIcon" : "ToggleButtonIcon"; return <>{React.createElement(Icons[iconName])}; }; @@ -65,7 +65,7 @@ class ToggleButton extends Component { onChange={this.onInputChange} {...this.props} /> - + {this.props.label && ( Date: Mon, 5 Aug 2019 16:09:12 +0300 Subject: [PATCH 2/4] Web: Components: added Tab component and svg icon --- .../src/components/icons/svg/index.js | 7 ++ .../src/components/icons/svg/tabs.react.svg | 3 + .../src/components/tabs/index.js | 99 +++++++++++++++++++ web/ASC.Web.Components/src/index.js | 1 + web/ASC.Web.Storybook/stories/tabs/README.md | 19 ++++ .../stories/tabs/index.stories.js | 90 +++++++++++++++++ 6 files changed, 219 insertions(+) create mode 100644 web/ASC.Web.Components/src/components/icons/svg/tabs.react.svg create mode 100644 web/ASC.Web.Components/src/components/tabs/index.js create mode 100644 web/ASC.Web.Storybook/stories/tabs/README.md create mode 100644 web/ASC.Web.Storybook/stories/tabs/index.stories.js diff --git a/web/ASC.Web.Components/src/components/icons/svg/index.js b/web/ASC.Web.Components/src/components/icons/svg/index.js index 1e6579d07b..2a80657b11 100644 --- a/web/ASC.Web.Components/src/components/icons/svg/index.js +++ b/web/ASC.Web.Components/src/components/icons/svg/index.js @@ -141,6 +141,7 @@ import OrigRadiobuttonHoverCheckedIcon from './radiobutton.hover.checked.react.s import OrigToggleButtonCheckedIcon from './toggle.button.checked.react.svg'; import OrigToggleButtonIcon from './toggle.button.react.svg'; +import OrigTabsIcon from './tabs.react.svg'; export const AZSortingIcon = createStyledIcon( OrigAZSortingIcon, @@ -698,4 +699,10 @@ export const ToggleButtonIcon = createStyledIcon( OrigToggleButtonIcon, 'ToggleButtonIcon', "rect" +); + +export const TabsIcon = createStyledIcon( + OrigTabsIcon, + 'TabsIcon', + "rect" ); \ No newline at end of file diff --git a/web/ASC.Web.Components/src/components/icons/svg/tabs.react.svg b/web/ASC.Web.Components/src/components/icons/svg/tabs.react.svg new file mode 100644 index 0000000000..9013ba9a16 --- /dev/null +++ b/web/ASC.Web.Components/src/components/icons/svg/tabs.react.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/ASC.Web.Components/src/components/tabs/index.js b/web/ASC.Web.Components/src/components/tabs/index.js new file mode 100644 index 0000000000..32f7317e39 --- /dev/null +++ b/web/ASC.Web.Components/src/components/tabs/index.js @@ -0,0 +1,99 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import styled, { css } from 'styled-components'; +import { Icons } from '../icons'; + +import { getCssFromSvg } from '../icons/get-css-from-svg'; +import ReactDOMServer from 'react-dom/server'; + + + +var tabsIcon/*, tabsIcon2*/; +(function () { tabsIcon = getCssFromSvg(ReactDOMServer.renderToString()); }()); +//tabsIcon2 = getCssFromSvg(ReactDOMServer.renderToString()); + +// Основной контейнер +const TabsContainer = styled.div``; + +// Шапка +const NavItem = styled.div` + position: relative; + box-shadow: 0px 5px 20px rgba(0,0,0,0.13); +`; + +//Исправить!!! +const hoverCss = css` + width: 80px; + height: 32px; + background-color: #F8F9F9; + border-radius: 16px; +`; + +// Заголовки шапки +const Label = styled.label` + margin:0; + min-width: 80px; + position: relative; + background-repeat: no-repeat; + p {text-align: center; margin-top: 6px;} + margin-right: 5px; + label {margin:0} + + ${props => props.selected ? + `background-image: url("data:image/svg+xml,${tabsIcon}"); cursor: default;` : + `background-image: none; + &:hover{ + cursor: pointer; + ${hoverCss} + }` + } +`; + +// Контенn в зависимости от шапки +const BodyContainer = styled.div``; + + +class Tabs extends Component { + constructor(props) { + super(props); + this.state = { + activeTab: '0' + }; + } + + labelClick = (tab) => { + if (this.state.activeTab !== tab.id) { + this.setState({ activeTab: tab.id }); + console.log('My update setState()'); + } + }; + + render() { + //console.log(this.props.selected); + + return ( + + + {this.props.children.map((item) => + + )} + + {this.props.children[this.state.activeTab].something_body} + + ); + } +} + +export default Tabs; + +Tabs.propTypes = { + children: PropTypes.object.isRequired +}; +Tabs.defaultProps = {/*isChecked: false*/ }; \ No newline at end of file diff --git a/web/ASC.Web.Components/src/index.js b/web/ASC.Web.Components/src/index.js index bf2d5c31c6..1bc73cac50 100644 --- a/web/ASC.Web.Components/src/index.js +++ b/web/ASC.Web.Components/src/index.js @@ -39,3 +39,4 @@ export { default as RadioButtonGroup } from './components/radio-button-group' export { default as RadioButton } from './components/radio-button' export { default as TextArea } from './components/text-area' export { default as ToggleButton } from './components/toggle-button' +export { default as Tabs } from './components/tabs' diff --git a/web/ASC.Web.Storybook/stories/tabs/README.md b/web/ASC.Web.Storybook/stories/tabs/README.md new file mode 100644 index 0000000000..45eeeaf47d --- /dev/null +++ b/web/ASC.Web.Storybook/stories/tabs/README.md @@ -0,0 +1,19 @@ +# Scrollbar + +#### Description + +Scrollbar is used for displaying custom scroollbar + +#### Usage + +```js +import { Scrollbar } from 'asc-web-components'; + +Some content +``` + +#### Properties + +| Props | Type | Required | Values | Default | Description | +| ---------- | ----------- | :------: | ----------------------------------------- | ------------ | --------------------- | +| `stype` | `string` | | `smallWhite`, `smallBlack`, `preMediumBlack`, `mediumBlack` | `smallBlack` | Scroollbar style type | \ No newline at end of file diff --git a/web/ASC.Web.Storybook/stories/tabs/index.stories.js b/web/ASC.Web.Storybook/stories/tabs/index.stories.js new file mode 100644 index 0000000000..411e183319 --- /dev/null +++ b/web/ASC.Web.Storybook/stories/tabs/index.stories.js @@ -0,0 +1,90 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { withKnobs, boolean, text, select } from '@storybook/addon-knobs/react'; +import { action } from '@storybook/addon-actions'; +import withReadme from 'storybook-readme/with-readme'; +import Readme from './README.md'; +import { Tabs, Text } from 'asc-web-components'; +import Section from '../../.storybook/decorators/section'; +import { BooleanValue } from 'react-values'; + +//{text("Body text", "Try again later")} + + +const something_items = [ + { + id: "0", + something_title: {text("Title text", "Title1")} , + something_body: +
+
+
+
+
+ }, + { + id: "1", + something_title: {text("Title text2", "Title2")} , + something_body: +
+
+
+
+
+ }, + { + id: "2", + something_title: {text("Title text3", "Title3")} , + something_body: +
+
+
+
+
+ }, + { + id: "3", + something_title: {text("Title text3", "Title3")} , + something_body: +
+
+
+
+
+ }, + { + id: "4", + something_title: {text("Title text3", "Title3")} , + something_body: +
+
+
+
+
+ } +]; +/* +const item = [{ + id: "0", + something_title: {text("Title text", "Title1")} , + something_body: +
+
+
+
+
+}]; +*/ + +storiesOf('Components|Tabs', module) + .addDecorator(withKnobs) + .addDecorator(withReadme(Readme)) + .add('base', () => { + return ( + + + {something_items} + + + ); + }); \ No newline at end of file From e9c564fda950e0ec99c73405d8f41e00bcf43bbd Mon Sep 17 00:00:00 2001 From: pavelbannov Date: Mon, 5 Aug 2019 16:45:33 +0300 Subject: [PATCH 3/4] People: method "get by filter" optimized --- .../Caching/CachedUserService.cs | 8 +- .../Context/Impl/UserManager.cs | 8 +- common/ASC.Core.Common/Core/IUserService.cs | 12 ++ common/ASC.Core.Common/Data/DbUserService.cs | 120 +++++++++++++++++- .../Server/Controllers/PeopleController.cs | 61 ++------- 5 files changed, 151 insertions(+), 58 deletions(-) diff --git a/common/ASC.Core.Common/Caching/CachedUserService.cs b/common/ASC.Core.Common/Caching/CachedUserService.cs index 2bdf280493..48588e7c8b 100644 --- a/common/ASC.Core.Common/Caching/CachedUserService.cs +++ b/common/ASC.Core.Common/Caching/CachedUserService.cs @@ -89,7 +89,12 @@ namespace ASC.Core.Caching { return (from == default(DateTime) ? users.Values : users.Values.Where(u => u.LastModified >= from)).ToDictionary(u => u.ID); } - } + } + + public IDictionary GetUsers(int tenant, bool isAdmin, EmployeeStatus? employeeStatus, List includeGroups, List excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total) + { + return service.GetUsers(tenant, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total); + } public UserInfo GetUser(int tenant, Guid id) { @@ -411,7 +416,6 @@ namespace ASC.Core.Caching return tenant.ToString() + USERS + userId; } - [Serializable] class UserPhoto { diff --git a/common/ASC.Core.Common/Context/Impl/UserManager.cs b/common/ASC.Core.Common/Context/Impl/UserManager.cs index 0d9f992b25..63ec349586 100644 --- a/common/ASC.Core.Common/Context/Impl/UserManager.cs +++ b/common/ASC.Core.Common/Context/Impl/UserManager.cs @@ -85,7 +85,13 @@ namespace ASC.Core break; } return users.ToArray(); - } + } + + public IEnumerable GetUsers(bool isAdmin, EmployeeStatus? employeeStatus, List includeGroups, List excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total) + { + var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId; + return userService.GetUsers(tenantId, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total).Values; + } public DateTime GetMaxUsersLastModified() { diff --git a/common/ASC.Core.Common/Core/IUserService.cs b/common/ASC.Core.Common/Core/IUserService.cs index cbf3c1ce07..7788a67b7d 100644 --- a/common/ASC.Core.Common/Core/IUserService.cs +++ b/common/ASC.Core.Common/Core/IUserService.cs @@ -33,6 +33,18 @@ namespace ASC.Core public interface IUserService { IDictionary GetUsers(int tenant, DateTime from); + + IDictionary GetUsers(int tenant, bool isAdmin, + EmployeeStatus? employeeStatus, + List includeGroups, + List excludeGroups, + EmployeeActivationStatus? activationStatus, + string text, + string sortBy, + bool sortOrderAsc, + long limit, + long offset, + out int total); UserInfo GetUser(int tenant, Guid id); diff --git a/common/ASC.Core.Common/Data/DbUserService.cs b/common/ASC.Core.Common/Data/DbUserService.cs index 6a5d2468f6..ed9c11f081 100644 --- a/common/ASC.Core.Common/Data/DbUserService.cs +++ b/common/ASC.Core.Common/Data/DbUserService.cs @@ -24,16 +24,16 @@ */ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; + using ASC.Common.Data.Sql; using ASC.Common.Data.Sql.Expressions; using ASC.Core.Tenants; using ASC.Core.Users; using ASC.Security.Cryptography; -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Text.RegularExpressions; namespace ASC.Core.Data { @@ -50,6 +50,112 @@ namespace ASC.Core.Data return ExecList(q).ConvertAll(ToUser).ToDictionary(u => u.ID); } + public IDictionary GetUsers(int tenant, bool isAdmin, + EmployeeStatus? employeeStatus, + List includeGroups, + List excludeGroups, + EmployeeActivationStatus? activationStatus, + string text, + string sortBy, + bool sortOrderAsc, + long limit, + long offset, + out int total) + { + var totalQuery = new SqlQuery("core_user u").Where("u.tenant", tenant).SelectCount(); + GetUserQueryForFilter(totalQuery, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text); + total = ExecScalar(totalQuery); + + var q = GetUserQuery(tenant, default); + + q = GetUserQueryForFilter(q, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text); + + if (!string.IsNullOrEmpty(sortBy)) + { + q.OrderBy(sortBy, sortOrderAsc); + } + + if(limit != 0) + { + q.SetMaxResults((int)limit); + } + + if(offset != 0) + { + q.SetFirstResult((int)offset); + } + + return ExecList(q).ConvertAll(ToUser).ToDictionary(u => u.ID); + } + + private SqlQuery GetUserQueryForFilter(SqlQuery q,bool isAdmin, + EmployeeStatus? employeeStatus, + List includeGroups, + List excludeGroups, + EmployeeActivationStatus? activationStatus, + string text) + { + q.Where("u.removed", false); + + if (includeGroups != null && includeGroups.Any()) + { + foreach (var g in includeGroups) + { + var groupQuery = new SqlQuery("core_usergroup cug") + .Where(Exp.EqColumns("cug.tenant", "cu.tenant")) + .Where(Exp.EqColumns("cu.id", "cug.userid")) + .Where(Exp.Eq("cug.groupid", g)); + q.Where(Exp.Exists(groupQuery)); + } + } + + if (excludeGroups != null && excludeGroups.Any()) + { + foreach (var g in excludeGroups) + { + var groupQuery = new SqlQuery("core_usergroup cug") + .Where(Exp.EqColumns("cug.tenant", "cu.tenant")) + .Where(Exp.EqColumns("cu.id", "cug.userid")) + .Where(Exp.Eq("cug.groupid", g)); + q.Where(!Exp.Exists(groupQuery)); + } + } + + if (employeeStatus != null) + { + switch (employeeStatus) + { + case EmployeeStatus.LeaveOfAbsence: + case EmployeeStatus.Terminated: + if (!isAdmin) q.Where("u.status", EmployeeStatus.Terminated); + break; + case EmployeeStatus.All: + if (!isAdmin) q.Where("u.status", EmployeeStatus.Active); + break; + case EmployeeStatus.Default: + case EmployeeStatus.Active: + q.Where("u.status", EmployeeStatus.Active); + break; + } + } + + if (activationStatus != null) + { + q.Where("u.activation_status", activationStatus.Value); + } + + if (!string.IsNullOrEmpty(text)) + { + q.Where(Exp.Like("u.firstname", text, SqlLike.AnyWhere) | + Exp.Like("u.lastname", text, SqlLike.AnyWhere) | + Exp.Like("u.title", text, SqlLike.AnyWhere) | + Exp.Like("u.location", text, SqlLike.AnyWhere) | + Exp.Like("u.email", text, SqlLike.AnyWhere)); + } + + return q; + } + public UserInfo GetUser(int tenant, Guid id) { var q = GetUserQuery(tenant, default(DateTime)).Where("id", id); @@ -329,11 +435,11 @@ namespace ASC.Core.Data var where = Exp.Empty; if (tenant != Tenant.DEFAULT_TENANT) { - where &= Exp.Eq("tenant", tenant); + where &= Exp.Eq("u.tenant", tenant); } if (from != default(DateTime)) { - where &= Exp.Ge("last_modified", from); + where &= Exp.Ge("u.last_modified", from); } if (where != Exp.Empty) { diff --git a/products/ASC.People/Server/Controllers/PeopleController.cs b/products/ASC.People/Server/Controllers/PeopleController.cs index 738cd831a1..76a94c1dd7 100644 --- a/products/ASC.People/Server/Controllers/PeopleController.cs +++ b/products/ASC.People/Server/Controllers/PeopleController.cs @@ -271,75 +271,40 @@ namespace ASC.Employee.Core.Controllers if (CoreContext.Configuration.Personal) throw new MethodAccessException("Method not available"); var isAdmin = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin() || WebItemSecurity.IsProductAdministrator(WebItemManager.PeopleProductID, SecurityContext.CurrentAccount.ID); - var status = isAdmin ? EmployeeStatus.All : EmployeeStatus.Default; - if (employeeStatus != null) + var includeGroups = new List(); + if (groupId.HasValue) { - switch (employeeStatus) - { - case EmployeeStatus.Terminated: - case EmployeeStatus.All: - status = isAdmin ? (EmployeeStatus)employeeStatus : EmployeeStatus.Default; - break; - default: - status = (EmployeeStatus)employeeStatus; - break; - } + includeGroups.Add(groupId.Value); } - var users = string.IsNullOrEmpty(ApiContext.FilterValue) ? - CoreContext.UserManager.GetUsers(status).AsEnumerable() : - CoreContext.UserManager.Search(ApiContext.FilterValue, status).AsEnumerable(); + var excludeGroups = new List(); - if (groupId != null && !groupId.Equals(Guid.Empty)) - { - users = users.Where(x => CoreContext.UserManager.IsUserInGroup(x.ID, (Guid)groupId)); - } - if (activationStatus != null) - { - users = activationStatus == EmployeeActivationStatus.Activated ? - users.Where(x => x.ActivationStatus.HasFlag(EmployeeActivationStatus.Activated)) : - users.Where(x => x.ActivationStatus == EmployeeActivationStatus.NotActivated || - x.ActivationStatus == EmployeeActivationStatus.Pending || - x.ActivationStatus == EmployeeActivationStatus.AutoGenerated); - } if (employeeType != null) { switch (employeeType) { case EmployeeType.User: - users = users.Where(x => !x.IsVisitor()); + excludeGroups.Add(Constants.GroupVisitor.ID); break; case EmployeeType.Visitor: - users = users.Where(x => x.IsVisitor()); + includeGroups.Add(Constants.GroupVisitor.ID); break; } } if (isAdministrator.HasValue && isAdministrator.Value) { - users = users.Where(x => x.IsAdmin() || x.GetListAdminModules().Any()); + includeGroups.Add(Constants.GroupAdmin.ID); + + var products = WebItemManager.Instance.GetItemsAll().Where(i => i is IProduct || i.ID == WebItemManager.MailProductID); + + includeGroups.AddRange(products.Select(r=> r.ID)); } - ApiContext.TotalCount = users.Count(); + var users = CoreContext.UserManager.GetUsers(isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, ApiContext.FilterValue, ApiContext.SortBy, !ApiContext.SortDescending, ApiContext.Count - 1, ApiContext.StartIndex, out int total); - switch (ApiContext.SortBy) - { - case "firstname": - users = ApiContext.SortDescending ? users.OrderByDescending(r => r, UserInfoComparer.FirstName) : users.OrderBy(r => r, UserInfoComparer.FirstName); - break; - case "lastname": - users = ApiContext.SortDescending ? users.OrderByDescending(r => r, UserInfoComparer.LastName) : users.OrderBy(r => r, UserInfoComparer.LastName); - break; - default: - users = ApiContext.SortDescending ? users.OrderByDescending(r => r, UserInfoComparer.Default) : users.OrderBy(r => r, UserInfoComparer.Default); - break; - } - - users = users.Skip((int)ApiContext.StartIndex).Take((int)ApiContext.Count - 1); - - //ApiContext.SetDataSorted(); - //ApiContext.SetDataPaginated(); + ApiContext.SetTotalCount(total); return users; } From 907f97bb780057bdf7aee07d83a1c4b827fe49c2 Mon Sep 17 00:00:00 2001 From: gopienkonikita Date: Mon, 5 Aug 2019 17:41:39 +0300 Subject: [PATCH 4/4] Web: Components: update tabs text color --- .../src/components/tabs/index.js | 12 ++---- .../stories/tabs/index.stories.js | 38 +++++++++---------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/web/ASC.Web.Components/src/components/tabs/index.js b/web/ASC.Web.Components/src/components/tabs/index.js index 32f7317e39..16f8d2af82 100644 --- a/web/ASC.Web.Components/src/components/tabs/index.js +++ b/web/ASC.Web.Components/src/components/tabs/index.js @@ -2,12 +2,9 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import styled, { css } from 'styled-components'; import { Icons } from '../icons'; - import { getCssFromSvg } from '../icons/get-css-from-svg'; import ReactDOMServer from 'react-dom/server'; - - var tabsIcon/*, tabsIcon2*/; (function () { tabsIcon = getCssFromSvg(ReactDOMServer.renderToString()); }()); //tabsIcon2 = getCssFromSvg(ReactDOMServer.renderToString()); @@ -40,7 +37,7 @@ const Label = styled.label` label {margin:0} ${props => props.selected ? - `background-image: url("data:image/svg+xml,${tabsIcon}"); cursor: default;` : + `background-image: url("data:image/svg+xml,${tabsIcon}"); cursor: default; p {color: #fff}` : `background-image: none; &:hover{ cursor: pointer; @@ -70,7 +67,6 @@ class Tabs extends Component { render() { //console.log(this.props.selected); - return ( @@ -81,11 +77,11 @@ class Tabs extends Component { onClick={() => { this.labelClick(item); }}> - {item.something_title} + {item.title} )} - {this.props.children[this.state.activeTab].something_body} + {this.props.children[this.state.activeTab].body} ); } @@ -94,6 +90,6 @@ class Tabs extends Component { export default Tabs; Tabs.propTypes = { - children: PropTypes.object.isRequired + children: PropTypes.object }; Tabs.defaultProps = {/*isChecked: false*/ }; \ No newline at end of file diff --git a/web/ASC.Web.Storybook/stories/tabs/index.stories.js b/web/ASC.Web.Storybook/stories/tabs/index.stories.js index 411e183319..2180e731ad 100644 --- a/web/ASC.Web.Storybook/stories/tabs/index.stories.js +++ b/web/ASC.Web.Storybook/stories/tabs/index.stories.js @@ -14,8 +14,8 @@ import { BooleanValue } from 'react-values'; const something_items = [ { id: "0", - something_title: {text("Title text", "Title1")} , - something_body: + title: Title1 , + body:
@@ -24,8 +24,8 @@ const something_items = [ }, { id: "1", - something_title: {text("Title text2", "Title2")} , - something_body: + title: Title2 , + body:
@@ -34,8 +34,8 @@ const something_items = [ }, { id: "2", - something_title: {text("Title text3", "Title3")} , - something_body: + title: Title3 , + body:
@@ -44,22 +44,22 @@ const something_items = [ }, { id: "3", - something_title: {text("Title text3", "Title3")} , - something_body: + title: Title4 , + body:
-
-
-
+
+
+
}, { id: "4", - something_title: {text("Title text3", "Title3")} , - something_body: + title: Title5 , + body:
-
-
-
+
+
+
} ]; @@ -82,9 +82,9 @@ storiesOf('Components|Tabs', module) .add('base', () => { return ( - - {something_items} - + + {something_items} + ); }); \ No newline at end of file