Merge branch 'master' into feature/asc-web-common

# Conflicts:
#	web/ASC.Web.Client/src/components/pages/Settings/Layout/index.js
This commit is contained in:
Alexey Safronov 2019-11-25 15:18:34 +03:00
commit d13e729282
32 changed files with 1042 additions and 207 deletions

View File

@ -352,7 +352,7 @@ class ProfileInfo extends React.PureComponent {
offsetLeft={50}
offsetRight={0}
tooltipContent={tooltipLanguage}
HelpButtonHeaderContent={t('Language')}
helpButtonHeaderContent={t('Language')}
/>
</TooltipIcon>

View File

@ -22,7 +22,7 @@ class RadioField extends React.Component {
radioOnChange,
tooltipContent,
HelpButtonHeaderContent
helpButtonHeaderContent
} = this.props;
return (
@ -31,7 +31,7 @@ class RadioField extends React.Component {
hasError={hasError}
labelText={labelText}
tooltipContent={tooltipContent}
HelpButtonHeaderContent={HelpButtonHeaderContent}
helpButtonHeaderContent={helpButtonHeaderContent}
>
<RadioButtonGroup
name={radioName}

View File

@ -33,7 +33,7 @@ class TextChangeField extends React.Component {
buttonTabIndex,
tooltipContent,
HelpButtonHeaderContent
helpButtonHeaderContent
} = this.props;
return (
@ -42,7 +42,7 @@ class TextChangeField extends React.Component {
hasError={hasError}
labelText={labelText}
tooltipContent={tooltipContent}
HelpButtonHeaderContent={HelpButtonHeaderContent}
helpButtonHeaderContent={helpButtonHeaderContent}
>
<InputContainer>
<TextInput

View File

@ -22,7 +22,7 @@ class TextField extends React.Component {
inputAutoFocussed,
inputTabIndex,
tooltipContent,
HelpButtonHeaderContent
helpButtonHeaderContent
} = this.props;
return (
@ -31,7 +31,7 @@ class TextField extends React.Component {
hasError={hasError}
labelText={labelText}
tooltipContent={tooltipContent}
HelpButtonHeaderContent={HelpButtonHeaderContent}
helpButtonHeaderContent={helpButtonHeaderContent}
>
<TextInput
name={inputName}

View File

@ -371,7 +371,7 @@ class CreateUserForm extends React.Component {
inputOnChange={this.onInputChange}
inputTabIndex={3}
HelpButtonHeaderContent={t("Mail")}
helpButtonHeaderContent={t("Mail")}
tooltipContent={
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications. <p className="tooltip_email" style={{marginTop: "1rem", marginBottom: "1rem"}} >You can create a new mail on the domain as the primary. In this case, you must set a one-time password so that the user can log in to the portal for the first time.</p> The main e-mail can be used as a login when logging in to the portal.

View File

@ -443,35 +443,70 @@ class UpdateUserForm extends React.Component {
const contacts = getUserContacts(profile.contacts);
const tooltipTypeContent =
<>
<Text.Body style={{paddingBottom: 17}} fontSize={13}>{t("ProfileTypePopupHelper")}</Text.Body>
<Table>
<tbody>
<tr>
<Th>{t("ProductsAndInstruments_Products")}</Th><Th>{t("Employee")}</Th><Th>{t("GuestCaption")}</Th>
</tr>
<tr>
<Td>{t("Mail")}</Td><Td>review</Td><Td>-</Td>
</tr>
<tr>
<Td>{t("DocumentsProduct")}</Td><Td>full access</Td><Td>view</Td>
</tr>
<tr>
<Td>{t("ProjectsProduct")}</Td><Td>review</Td><Td>-</Td>
</tr>
<tr>
<Td>{t("CommunityProduct")}</Td><Td>full access</Td><Td>view</Td>
</tr>
<tr>
<Td>{t("People")}</Td><Td>review</Td><Td>-</Td>
</tr>
<tr>
<Td>{t("Message")}</Td><Td>review</Td><Td>review</Td>
</tr>
<tr>
<Td>{t("Calendar")}</Td><Td>review</Td><Td>review</Td>
</tr>
</tbody>
</Table>
<Text.Body
style={{paddingBottom: 17}}
fontSize={13}>
{t("ProfileTypePopupHelper")}
</Text.Body>
<Text.Body fontSize={12}>
<Table>
<tbody>
<tr>
<Th>
<Text.Body isBold fontSize={13}>
{t("ProductsAndInstruments_Products")}
</Text.Body>
</Th>
<Th>
<Text.Body isBold fontSize={13}>
{t("Employee")}
</Text.Body>
</Th>
<Th>
<Text.Body isBold fontSize={13}>
{t("GuestCaption")}
</Text.Body>
</Th>
</tr>
<tr>
<Td>{t("Mail")}</Td>
<Td>review</Td>
<Td>-</Td>
</tr>
<tr>
<Td>{t("DocumentsProduct")}</Td>
<Td>full access</Td>
<Td>view</Td>
</tr>
<tr>
<Td>{t("ProjectsProduct")}</Td>
<Td>review</Td>
<Td>-</Td>
</tr>
<tr>
<Td>{t("CommunityProduct")}</Td>
<Td>full access</Td>
<Td>view</Td>
</tr>
<tr>
<Td>{t("People")}</Td>
<Td>review</Td>
<Td>-</Td>
</tr>
<tr>
<Td>{t("Message")}</Td>
<Td>review</Td>
<Td>review</Td>
</tr>
<tr>
<Td>{t("Calendar")}</Td>
<Td>review</Td>
<Td>review</Td>
</tr>
</tbody>
</Table>
</Text.Body>
<Link
color="#316DAA"
isHovered={true}
@ -517,11 +552,18 @@ class UpdateUserForm extends React.Component {
buttonOnClick={this.onEmailChange}
buttonTabIndex={1}
HelpButtonHeaderContent={t("Mail")}
helpButtonHeaderContent={t("Mail")}
tooltipContent={
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications. <p style={{height: "0", visibility: "hidden"}}>You can create a new mail on the domain as the primary. In this case, you must set a one-time password so that the user can log in to the portal for the first time.</p> The main e-mail can be used as a login when logging in to the portal.
</Trans>
<Text.Body fontSize={13}>
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications.
<p style={{marginTop: "1rem"/*, height: "0", visibility: "hidden"*/}}>
You can create a new mail on the domain as the primary.
In this case, you must set a one-time password so that the user can log in to the portal for the first time.
</p>
The main e-mail can be used as a login when logging in to the portal.
</Trans>
</Text.Body>
}
/>
<TextChangeField
@ -595,7 +637,7 @@ class UpdateUserForm extends React.Component {
radioOnChange={this.onUserTypeChange}
tooltipContent={tooltipTypeContent}
HelpButtonHeaderContent={t('UserType')}
helpButtonHeaderContent={t('UserType')}
/>
<DateField
calendarHeaderContent={t("CalendarSelectDate")}

View File

@ -178,7 +178,7 @@ class Customization extends React.Component {
className='margin-top field-container-width'
labelText={`${t("Language")}:`}
tooltipContent={tooltipLanguage}
HelpButtonHeaderContent={t("Language")}
helpButtonHeaderContent={t("Language")}
isVertical={true}>
<ComboBox
id='comboBoxLanguage'

View File

@ -1,9 +1,8 @@
import React, { lazy } from "react";
import React from "react";
import { Route, Switch } from "react-router-dom";
import { withRouter } from "react-router";
const CustomizationSettings = lazy(() => import("./customization"));
const WhiteLabel = lazy(() => import("./whitelabel"));
import CustomizationSettings from "./customization";
import WhiteLabel from "./whitelabel";
const Common = ({ match }) => {
const basePath = '/settings/common';
@ -15,13 +14,11 @@ const Common = ({ match }) => {
path={[`${basePath}/customization`, '/common', match.path]}
component={CustomizationSettings}
/>
<Route
exact
path={`${basePath}/whitelabel`}
component={WhiteLabel}
/>
</Switch>
);
};

View File

@ -1,4 +1,4 @@
import React, { Component, lazy } from "react";
import React, { Component } from "react";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import i18n from "../../i18n";
@ -6,9 +6,9 @@ import { I18nextProvider, withTranslation } from "react-i18next";
import styled from "styled-components";
import { TabContainer } from "asc-web-components";
const OwnerSettings = lazy(() => import("./sub-components/owner"));
const AdminsSettings = lazy(() => import("./sub-components/admins"));
const ModulesSettings = lazy(() => import("./sub-components/modules"));
import OwnerSettings from "./sub-components/owner";
import AdminsSettings from "./sub-components/admins";
import ModulesSettings from "./sub-components/modules";
const MainContainer = styled.div`
padding-bottom: 16px;

View File

@ -31,12 +31,6 @@ import {
import { getUserRole } from "../../../../../../store/settings/selectors";
import isEmpty from "lodash/isEmpty";
const AdminsContainer = styled.div`
.hidden-icon {
position: fixed;
visibility: hidden;
}
`;
const ToggleContentContainer = styled.div`
.buttons_container {
display: flex;
@ -337,10 +331,7 @@ class PureAdminsSettings extends Component {
console.log("Admins render_");
return (
/*TODO: delete after resolve icon button problem*/
<AdminsContainer>
<IconButton className="hidden-icon" iconName="SearchIcon" />
<>
{showLoader ? (
<Loader className="pageLoader" type="rombs" size={40} />
) : (
@ -528,7 +519,7 @@ class PureAdminsSettings extends Component {
</ToggleContentContainer>
</>
)}
</AdminsContainer>
</>
);
}
}

View File

@ -1,35 +1,29 @@
import React, { lazy } from "react";
import React from "react";
import { Route, Switch, Redirect } from "react-router-dom";
import { withRouter } from "react-router";
import Layout from './Layout';
const CommonSettings = lazy(() => import("./categories/common"));
const SecuritySettings = lazy(() => import("./categories/security"));
import CommonSettings from "./categories/common";
import SecuritySettings from "./categories/security";
const Settings = () => {
const basePath = '/settings';
return (
<Layout key='1'>
<Switch>
<Route
path={`${basePath}/security`}
component={SecuritySettings}
/>
<Route
path={[`${basePath}/common`, basePath]}
component={CommonSettings}
/>
<Redirect
to={{
pathname: "/error/404",
}}
/>
</Switch>
</Layout>
);

View File

@ -275,7 +275,7 @@ const Form = props => {
/>
<TooltipStyle>
<HelpButton
HelpButtonHeaderContent={t('CookieSettingsTitle')}
helpButtonHeaderContent={t('CookieSettingsTitle')}
tooltipContent={
<Text.Body fontSize={12}>{t("RememberHelper")}</Text.Body>
}

View File

@ -1,6 +1,6 @@
{
"name": "asc-web-components",
"version": "1.0.185",
"version": "1.0.190",
"description": "Ascensio System SIA component library",
"license": "AGPL-3.0",
"main": "dist/asc-web-components.js",
@ -97,6 +97,7 @@
"dependencies": {
"bootstrap": "^4.3.1",
"email-addresses": "^3.0.3",
"html-to-react": "^1.4.2",
"lodash": "4.17.15",
"lodash-es": "4.17.15",
"moment": "^2.24.0",

View File

@ -25,6 +25,8 @@ class AvatarEditor extends React.Component {
this.onLoadFileError = this.onLoadFileError.bind(this);
this.onLoadFile = this.onLoadFile.bind(this);
this.onPositionChange = this.onPositionChange.bind(this);
this.onSizeChange = this.onSizeChange.bind(this);
this.onDeleteImage = this.onDeleteImage.bind(this);
}
@ -40,6 +42,9 @@ class AvatarEditor extends React.Component {
})
if (typeof this.props.onDeleteImage === 'function') this.props.onDeleteImage();
}
onSizeChange(data){
this.setState(data);
}
onPositionChange(data) {
this.setState(data);
}
@ -83,6 +88,7 @@ class AvatarEditor extends React.Component {
<AvatarEditorBody
onImageChange={this.onImageChange}
onPositionChange={this.onPositionChange}
onSizeChange={this.onSizeChange}
onLoadFileError={this.onLoadFileError}
onLoadFile={this.onLoadFile}
deleteImage={this.onDeleteImage}

View File

@ -190,6 +190,10 @@ class AvatarEditorBody extends React.Component {
this.setState({
scale: scale < 1 ? 1 : scale > 5 ? 5 : scale
});
this.props.onSizeChange({
width: this.setEditorRef.current.getImage().width,
height: this.setEditorRef.current.getImage().height
});
}
}
onTouchEnd(evt) {
@ -217,6 +221,10 @@ class AvatarEditorBody extends React.Component {
this.setState({
scale: scale < 1 ? 1 : scale > 5 ? 5 : scale
});
this.props.onSizeChange({
width: this.setEditorRef.current.getImage().width,
height: this.setEditorRef.current.getImage().height
});
}
handleScale = e => {
@ -319,6 +327,7 @@ class AvatarEditorBody extends React.Component {
AvatarEditorBody.propTypes = {
onImageChange: PropTypes.func,
onPositionChange: PropTypes.func,
onSizeChange: PropTypes.func,
onLoadFileError: PropTypes.func,
onLoadFile: PropTypes.func,
deleteImage: PropTypes.func,

View File

@ -22,6 +22,6 @@ import { FieldContainer } from "asc-web-components";
| `isRequired` | `bool` | - | - | `false` | Indicates that the field is required to fill |
| `hasError` | `bool` | - | - | `false` | Indicates that the field is incorrect |
| `labelText` | `string` | - | - | - | Field label text |
| `tooltipContent` | `object`,`string` | | - | - | Tooltip content |
| `HelpButtonHeaderContent` | `string` | - | - | - | Tooltip header content (tooltip opened in aside) |
| `tooltipContent` | `object`,`string` | - | - | - | Tooltip content |
| `helpButtonHeaderContent` | `string` | - | - | - | Tooltip header content (tooltip opened in aside) |
| `horLabelWidth` | `string` | - | - | `110px` | Label width in horizontal alignment |

View File

@ -13,33 +13,6 @@ storiesOf("Components|FieldContainer", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => (
<StringValue
onChange={e => {
action("onChange")(e);
}}
>
{({ value, set }) => (
<Section>
<FieldContainer
isVertical={boolean("isVertical", false)}
isRequired={boolean("isRequired", false)}
hasError={boolean("hasError", false)}
labelText={text("labelText", "Name:")}
>
<TextInput
value={value}
hasError={boolean("hasError", false)}
className="field-input"
onChange={e => {
set(e.target.value);
}}
/>
</FieldContainer>
</Section>
)}
</StringValue>
))
.add("with tooltip", () => (
<StringValue
onChange={e => {
action("onChange")(e);
@ -53,26 +26,9 @@ storiesOf("Components|FieldContainer", module)
isRequired={boolean("isRequired", false)}
hasError={boolean("hasError", false)}
labelText={text("labelText", "Name:")}
tooltipContent={"Paste you tooltip content here"}
place="top"
>
<TextInput
value={value}
hasError={boolean("hasError", false)}
className="field-input"
onChange={e => {
set(e.target.value);
}}
/>
</FieldContainer>
</div>
<div style={{ marginTop: 200, marginLeft: 50 }}>
<FieldContainer
isVertical={boolean("isVertical", false)}
isRequired={boolean("isRequired", false)}
hasError={boolean("hasError", false)}
labelText={text("labelText", "Name:")}
tooltipContent={"Paste you tooltip content here"}
horLabelWidth={text("horLabelWidth", "110px")}
tooltipContent={text("tooltipContent", "Paste you tooltip content here")}
helpButtonHeaderContent={text("helpButtonHeaderContent", "Tooltip header")}
place="top"
>
<TextInput

View File

@ -85,7 +85,7 @@ class FieldContainer extends React.Component {
children,
tooltipContent,
place,
HelpButtonHeaderContent,
helpButtonHeaderContent,
horLabelWidth
} = this.props;
@ -103,7 +103,7 @@ class FieldContainer extends React.Component {
<HelpButton
tooltipContent={tooltipContent}
place={place}
HelpButtonHeaderContent={HelpButtonHeaderContent}
helpButtonHeaderContent={helpButtonHeaderContent}
/>
)}
</div>
@ -128,7 +128,7 @@ FieldContainer.propTypes = {
]),
tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
place: PropTypes.string,
HelpButtonHeaderContent: PropTypes.string,
helpButtonHeaderContent: PropTypes.string,
horLabelWidth: PropTypes.string
};

View File

@ -52,4 +52,4 @@ import { HelpButton } from "asc-web-components";
| `tooltipContent` | `object`,`string` | ✅ | - | - | Tooltip content |
| `place` | `string` | - | `top`, `right`, `bottom`, `left` | `top` | Tooltip placement |
| `displayType` | `oneOf` | - | `dropdown`, `aside`, `auto` | `auto` | Tooltip display type |
| `HelpButtonHeaderContent` | `string` | - | - | - | Tooltip header content (tooltip opened in aside) |
| `helpButtonHeaderContent` | `string` | - | - | - | Tooltip header content (tooltip opened in aside) |

View File

@ -36,7 +36,7 @@ storiesOf("Components|Buttons", module)
/>
<HelpButton
displayType="aside"
HelpButtonHeaderContent="Aside position HelpButton"
helpButtonHeaderContent="Aside position HelpButton"
tooltipContent={
<Text.Body>
You tooltip content with{" "}
@ -51,7 +51,7 @@ storiesOf("Components|Buttons", module)
/>
<HelpButton
displayType="auto"
HelpButtonHeaderContent="Auto position HelpButton"
helpButtonHeaderContent="Auto position HelpButton"
tooltipContent={
<>
<p>You can put every thing here</p>

View File

@ -125,7 +125,7 @@ class HelpButton extends React.Component {
offsetRight,
offsetLeft,
zIndex,
HelpButtonHeaderContent
helpButtonHeaderContent
} = this.props;
return (
@ -156,13 +156,16 @@ class HelpButton extends React.Component {
<Backdrop onClick={this.onClose} visible={isOpen} zIndex={zIndex} />
<Aside visible={isOpen} scale={false} zIndex={zIndex}>
<Content>
<Header>
<HeaderText>
<Text.Body isBold={true} fontSize={21}>
{HelpButtonHeaderContent}
</Text.Body>
</HeaderText>
</Header>
{
helpButtonHeaderContent &&
<Header>
<HeaderText>
<Text.Body isBold={true} fontSize={21}>
{helpButtonHeaderContent}
</Text.Body>
</HeaderText>
</Header>
}
<Body>{tooltipContent}</Body>
</Content>
</Aside>
@ -178,7 +181,7 @@ HelpButton.propTypes = {
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]),
tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
offsetRight: PropTypes.number,
tooltipMaxWidth: PropTypes.number,
tooltipId: PropTypes.string,
@ -186,7 +189,7 @@ HelpButton.propTypes = {
offsetLeft: PropTypes.number,
zIndex: PropTypes.number,
displayType: PropTypes.oneOf(["dropdown", "aside", "auto"]),
HelpButtonHeaderContent: PropTypes.string
helpButtonHeaderContent: PropTypes.string
};
HelpButton.defaultProps = {

View File

@ -1,6 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import ReactDOMServer from 'react-dom/server';
import {Parser} from 'html-to-react'
const iconSizes = {
small: 12,
@ -38,11 +40,33 @@ const getSizeStyle = size => {
export default function createStyledIcon(Component, displayName, fillPath="*", strokePath="*") {
const Icon = ({ isfill, isStroke, color, stroke, fillPath, strokePath, ...props }) => {
//console.log(`Icon render ${displayName}`);
return (<Component {...props}></Component>);
};
class Icon extends React.Component {
render_xml(id, xml_string){
var doc = new DOMParser().parseFromString(xml_string, 'application/xml');
var el = document.getElementById(id)
el.appendChild(
el.ownerDocument.importNode(doc.documentElement, true)
)
}
render() {
const { isfill, isStroke, color, stroke, fillPath, strokePath, ...props } = this.props;
var svg = ReactDOMServer.renderToString(<Component {...props}></Component>);
const matchResult = svg.match(/\s*mask id="(\w*)"\s/);
if(matchResult != null){
if(matchResult.length > 1){
svg = svg.replace(new RegExp(matchResult[1],'g'), Math.random().toString(36).substring(2, 5) + Math.random().toString(36).substring(2, 5))
var htmlToReactParser = new Parser();
var reactComponent = htmlToReactParser.parse(svg);
return reactComponent;
}
}
return (<Component {...props}></Component>);
}
}
const StyledIcon = styled(Icon)(
props => `
${props.fillPath} {

View File

@ -2,12 +2,162 @@ import React from 'react';
import { mount } from 'enzyme';
import Layout from '.';
const baseProps = {
isBackdropVisible: false,
isNavHoverEnabled: true,
isNavOpened: false,
isAsideVisible: false,
currentUser: null,
currentUserActions: [],
availableModules: []
}
// eslint-disable-next-line no-undef
const later = (delay) => new Promise(function (resolve) {
setTimeout(resolve, delay);
});
describe('<Layout />', () => {
it('renders without error', () => {
const wrapper = mount(
<Layout />
<Layout {...baseProps} />
);
expect(wrapper).toExist();
});
it('renders with modules', () => {
const modules = [
{
id: 'test',
separator: true
},
{
id: 'demo',
title: 'demo',
iconName: 'CalendarCheckedIcon',
notifications: 0
},
{
id: 'demo1',
title: 'demo1',
iconName: 'CalendarCheckedIcon',
notifications: 0,
isolateMode: true
}
];
const wrapper = mount(
<Layout availableModules={modules} currentModuleId='demo' />
).instance();
expect(wrapper.props.availableModules).toBe(modules);
});
it("componentDidUpdate() test", () => {
const wrapper = mount(
<Layout {...baseProps} />
).instance();
wrapper.componentDidUpdate(wrapper.props);
expect(wrapper.props).toBe(wrapper.props);
wrapper.componentDidUpdate(
{
currentModuleId: 'demo',
currentUser: {
id: 'test',
displayName: 'test test',
email: 'test',
avatarSmall: 'test'
},
availableModules: [
{
id: 'test',
separator: true
}
]
});
expect(wrapper.props).toBe(wrapper.props);
});
it("call backdropClick()", () => {
const wrapper = mount(
<Layout {...baseProps} />
).instance();
wrapper.backdropClick();
expect(wrapper.state.isBackdropVisible).toBe(false);
expect(wrapper.state.isNavOpened).toBe(false);
expect(wrapper.state.isAsideVisible).toBe(false);
});
it("call showNav()", () => {
const wrapper = mount(
<Layout {...baseProps} />
).instance();
wrapper.showNav();
expect(wrapper.state.isBackdropVisible).toBe(true);
expect(wrapper.state.isNavOpened).toBe(true);
expect(wrapper.state.isAsideVisible).toBe(false);
expect(wrapper.state.isNavHoverEnabled).toBe(false);
});
it("call toggleAside()", () => {
const wrapper = mount(
<Layout {...baseProps} />
).instance();
wrapper.toggleAside();
expect(wrapper.state.isBackdropVisible).toBe(true);
expect(wrapper.state.isNavOpened).toBe(false);
expect(wrapper.state.isAsideVisible).toBe(true);
expect(wrapper.state.isNavHoverEnabled).toBe(false);
});
it("call handleNavMouseEnter()", async () => {
const wrapper = mount(
<Layout {...baseProps} />
).instance();
wrapper.setState({ isNavHoverEnabled: false });
wrapper.handleNavMouseEnter();
expect(wrapper.state.isNavOpened).toBe(false);
wrapper.setState({ isNavHoverEnabled: true });
wrapper.handleNavMouseEnter();
await later(400);
expect(wrapper.state.isNavOpened).toBe(true);
});
it("call handleNavMouseLeave()", () => {
const wrapper = mount(
<Layout {...baseProps} />
).instance();
wrapper.setState({ isNavHoverEnabled: false });
wrapper.handleNavMouseLeave();
expect(wrapper.state.isNavOpened).toBe(false);
wrapper.setState({ isNavHoverEnabled: true });
wrapper.timeout = 1;
wrapper.handleNavMouseLeave();
expect(wrapper.state.isNavOpened).toBe(false);
});
});

View File

@ -0,0 +1,31 @@
# PageLayout
Default page layout
### Usage
```js
import { PageLayout } from "asc-web-components";
```
```jsx
<PageLayout
articleHeaderContent={articleHeaderContent}
articleMainButtonContent={articleMainButtonContent}
articleBodyContent={articleBodyContent}
sectionHeaderContent={sectionHeaderContent}
sectionFilterContent={sectionFilterContent}
sectionBodyContent={sectionBodyContent}
sectionPagingContent={sectionPagingContent}
/>
```
### Properties
| Props | Type | Required | Values | Default | Description |
| ------------------- | :----: | :------: | :----: | :-----: | ----------------------------------------- |
| `isBackdropVisible` | `bool` | - | - | `false` | If you need display Backdrop |
| `isNavHoverEnabled` | `bool` | - | - | `true` | If you need hover navigation on Backdrop |
| `isNavOpened` | `bool` | - | - | `false` | If you need display navigation |
| `isAsideVisible` | `bool` | - | - | `false` | If you need display aside |
| `withBodyScroll` | `bool` | - | - | `true` | If you need display scroll inside content |

View File

@ -0,0 +1,141 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import styled from '@emotion/styled';
import Layout from '../layout';
import PageLayout from '.';
import { Text } from '../text';
import IconButton from '../icon-button';
import ContextMenuButton from '../context-menu-button';
import MainButton from '../main-button';
import SearchInput from '../search-input';
import Paging from '../paging';
import withReadme from 'storybook-readme/with-readme';
import { boolean, withKnobs } from '@storybook/addon-knobs/react';
import Readme from './README.md';
const HeaderContent = styled.div`
display: flex;
align-items: center;
& > * {
margin-right: 8px !important;
}
`;
const pageItems = [
{
key: '1',
label: '1 of 2',
onClick: (e) => action('set paging 1 of 2')(e)
},
{
key: '2',
label: '2 of 2',
onClick: (e) => action('set paging 2 of 2')(e)
}
];
const perPageItems = [
{
key: '1-1',
label: '25 per page',
onClick: (e) => action('set paging 25 action')(e)
},
{
key: '1-2',
label: '50 per page',
onClick: (e) => action('set paging 50 action')(e)
}
];
const articleHeaderContent = <Text.MenuHeader>Article Header</Text.MenuHeader>;
const articleMainButtonContent = <MainButton
text='Actions'
clickAction={(e) => action('MainButton Clicked')(e)}
/>;
const articleBodyContent = <p style={{ padding: 40 }}>Article Content</p>;
const sectionHeaderContent = <HeaderContent>
<IconButton
iconName={"ArrowPathIcon"}
size='16'
onClick={(e) => action('ArrowPathIcon Clicked')(e)}
/>
<Text.ContentHeader>Section Header</Text.ContentHeader>
<IconButton
iconName={"PlusIcon"}
size='16'
onClick={(e) => action('PlusIcon Clicked')(e)}
/>
<ContextMenuButton
title="Actions"
getData={() => [
{
key: 'key',
label: 'label',
onClick: (e) => action('label Clicked')(e)
}
]}
/>
</HeaderContent>;
const sectionFilterContent = <SearchInput
isNeedFilter={true}
getFilterData={() => [
{
key: 'filter-example',
group: 'filter-example',
label: 'example group',
isHeader: true
},
{
key: 'filter-example-test',
group: 'filter-example',
label: 'Test'
}
]}
onSearchClick={(result) => { console.log(result) }}
onChangeFilter={(result) => { console.log(result) }}
/>
const sectionBodyContent = <p style={{ padding: 40 }}>Section Content</p>;
const sectionPagingContent = <Paging
previousLabel="Previous"
nextLabel="Next"
pageItems={pageItems}
perPageItems={perPageItems}
selectedPageItem={pageItems[0]}
selectedCountItem={perPageItems[0]}
onSelectPage={(a) => console.log(a)}
onSelectCount={(a) => console.log(a)}
previousAction={(e) => action('Prev Clicked')(e)}
nextAction={(e) => action('Next Clicked')(e)}
openDirection="top"
/>
storiesOf('Components|PageLayout', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('base', () => (
<Layout
isBackdropVisible={boolean("isBackdropVisible", false)}
isNavHoverEnabled={boolean("isNavHoverEnabled", true)}
isNavOpened={boolean("isNavOpened", false)}
isAsideVisible={boolean("isAsideVisible", false)}
>
<PageLayout
articleHeaderContent={articleHeaderContent}
articleMainButtonContent={articleMainButtonContent}
articleBodyContent={articleBodyContent}
sectionHeaderContent={sectionHeaderContent}
sectionFilterContent={sectionFilterContent}
sectionBodyContent={sectionBodyContent}
sectionPagingContent={sectionPagingContent}
/>
</Layout>
));

View File

@ -0,0 +1,102 @@
import React from 'react';
import { mount } from 'enzyme';
import PageLayout from '.';
const baseProps = {
isBackdropVisible: false,
isArticleVisible: false,
isArticlePinned: false,
withBodyScroll: true
}
describe('<PageLayout />', () => {
it('renders without error', () => {
const wrapper = mount(
<PageLayout {...baseProps} />
);
expect(wrapper).toExist();
});
it("componentDidUpdate() test re-render", () => {
const wrapper = mount(
<PageLayout {...baseProps} />
).instance();
wrapper.componentDidUpdate({ withBodyScroll: false });
expect(wrapper.props).toBe(wrapper.props);
});
it("componentDidUpdate() test no re-render", () => {
const wrapper = mount(
<PageLayout
{...baseProps}
articleHeaderContent={<>1</>}
articleMainButtonContent={<>2</>}
articleBodyContent={<>3</>}
sectionHeaderContent={<>4</>}
sectionFilterContent={<>5</>}
sectionBodyContent={<>6</>}
sectionPagingContent={<>7</>}
withBodyScroll={false}
/>
).instance();
wrapper.componentDidUpdate(wrapper.props);
expect(wrapper.props.withBodyScroll).toBe(false);
wrapper.componentDidUpdate(wrapper.props);
expect(wrapper.props).toBe(wrapper.props);
});
it("call backdropClick()", () => {
const wrapper = mount(
<PageLayout {...baseProps} />
).instance();
wrapper.backdropClick();
expect(wrapper.state.isBackdropVisible).toBe(false);
expect(wrapper.state.isArticleVisible).toBe(false);
expect(wrapper.state.isArticlePinned).toBe(false);
});
it("call pinArticle()", () => {
const wrapper = mount(
<PageLayout {...baseProps} />
).instance();
wrapper.pinArticle();
expect(wrapper.state.isBackdropVisible).toBe(false);
expect(wrapper.state.isArticleVisible).toBe(true);
expect(wrapper.state.isArticlePinned).toBe(true);
});
it("call unpinArticle()", () => {
const wrapper = mount(
<PageLayout {...baseProps} />
).instance();
wrapper.unpinArticle();
expect(wrapper.state.isBackdropVisible).toBe(true);
expect(wrapper.state.isArticleVisible).toBe(true);
expect(wrapper.state.isArticlePinned).toBe(false);
});
it("call showArticle()", () => {
const wrapper = mount(
<PageLayout {...baseProps} />
).instance();
wrapper.showArticle();
expect(wrapper.state.isBackdropVisible).toBe(true);
expect(wrapper.state.isArticleVisible).toBe(true);
expect(wrapper.state.isArticlePinned).toBe(false);
});
});

View File

@ -4,12 +4,11 @@ import styled from "styled-components";
import { Text } from "../text";
import Scrollbar from "../scrollbar";
const TabsContainer = styled.div`
.scrollbar {
width: 100% !important;
height: 50px !important;
}
const StyledScrollbar = styled(Scrollbar)`
width: 100% !important;
height: 50px !important;
`;
const NavItem = styled.div`
position: relative;
white-space: nowrap;
@ -76,7 +75,8 @@ class TabContainer extends Component {
}
this.state = {
activeTab: this.props.selectedItem
activeTab: this.props.selectedItem,
onScrollHide: true
};
this.scrollRef = React.createRef();
@ -89,39 +89,132 @@ class TabContainer extends Component {
delete newItem.content;
this.props.onSelect && this.props.onSelect(newItem);
const position = ref.current.offsetLeft - 40;
this.scrollRef.current.scrollLeft(position);
this.setTabPosition(index, ref);
}
};
getWidthElements = () => {
const arrayWidths = [];
const length = this.arrayRefs.length - 1;
let widthItem = 0;
while (length + 1 !== widthItem) {
arrayWidths.push(this.arrayRefs[widthItem].current.offsetWidth);
widthItem++;
}
return arrayWidths;
};
shouldComponentUpdate(nextProps, nextState) {
const { activeTab } = this.state;
const { activeTab, onScrollHide } = this.state;
const { isDisabled } = this.props;
if (
activeTab === nextState.activeTab &&
isDisabled === nextProps.isDisabled
isDisabled === nextProps.isDisabled &&
onScrollHide === nextState.onScrollHide
) {
return false;
}
return true;
}
componentDidMount() {
const { activeTab } = this.state;
if (activeTab !== 0 && this.arrayRefs[activeTab].current !== null) {
this.secondFunction(activeTab);
}
}
setTabPosition = (index, currentRef) => {
const arrayOfWidths = this.getWidthElements(); //get tabs widths
const scrollLeft = this.scrollRef.current.getScrollLeft(); // get scroll position relative to left side
const staticScroll = this.scrollRef.current.getScrollWidth(); //get static scroll width
const containerWidth = this.scrollRef.current.getClientWidth(); //get main container width
const currentTabWidth = currentRef.current.offsetWidth;
const marginRight = 8;
//get tabs of left side
let leftTabs = 0;
let leftFullWidth = 0;
while (leftTabs !== index) {
leftTabs++;
leftFullWidth += arrayOfWidths[leftTabs] + marginRight;
}
leftFullWidth += arrayOfWidths[0] + marginRight;
//get tabs of right side
let rightTabs = this.arrayRefs.length - 1;
let rightFullWidth = 0;
while (rightTabs !== index - 1) {
rightFullWidth += arrayOfWidths[rightTabs] + marginRight;
rightTabs--;
}
//Out of range of left side
if (leftFullWidth > containerWidth + scrollLeft) {
let prevIndex = index - 1;
let widthBlocksInContainer = 0;
while (prevIndex !== -1) {
widthBlocksInContainer += arrayOfWidths[prevIndex] + marginRight;
prevIndex--;
}
const difference = containerWidth - widthBlocksInContainer;
const currentContainerWidth = currentTabWidth;
this.scrollRef.current.scrollLeft(
difference * -1 + currentContainerWidth + marginRight
);
}
//Out of range of left side
else if (rightFullWidth > staticScroll - scrollLeft) {
this.scrollRef.current.scrollLeft(staticScroll - rightFullWidth);
}
};
secondFunction = index => {
const arrayOfWidths = this.getWidthElements(); //get tabs widths
const marginRight = 8;
let rightTabs = this.arrayRefs.length - 1;
let rightFullWidth = 0;
while (rightTabs !== index - 1) {
rightFullWidth += arrayOfWidths[rightTabs] + marginRight;
rightTabs--;
}
const staticScroll = this.scrollRef.current.getScrollWidth(); //get static scroll width
this.scrollRef.current.scrollLeft(staticScroll - rightFullWidth);
};
onMouseEnter = () => {
this.setState({ onScrollHide: false });
};
onMouseLeave = () => {
this.setState({ onScrollHide: true });
};
render() {
//console.log("Tabs container render");
const { isDisabled, children } = this.props;
const { activeTab } = this.state;
const { activeTab, onScrollHide } = this.state;
return (
<TabsContainer>
<Scrollbar
values={this.onScrollFrame}
autoHide
<>
<StyledScrollbar
autoHide={onScrollHide}
autoHideDuration={500}
autoHideTimeout={1000}
stype="preMediumBlack"
className="scrollbar"
ref={this.scrollRef}
>
<NavItem className="className_items">
<NavItem
onMouseMove={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
className="className_items"
>
{children.map((item, index) => (
<Label
ref={this.arrayRefs[index]}
@ -141,9 +234,9 @@ class TabContainer extends Component {
</Label>
))}
</NavItem>
</Scrollbar>
</StyledScrollbar>
<BodyContainer>{children[activeTab].content}</BodyContainer>
</TabsContainer>
</>
);
}
}

View File

@ -106,66 +106,289 @@ const scrollArrayItems = [
key: "tab0",
title: "First long tab container",
content: (
<div>
<button>button</button>
<button>button</button>
<button>button</button>
</div>
<>
<label>Tab_0 Tab_0 Tab_0</label>
<br />
<label>Tab_0 Tab_0 Tab_0</label>
<br />
<label>Tab_0 Tab_0 Tab_0</label>
</>
)
},
{
key: "tab1",
title: "Short",
content: (
<div>
<label>label</label>
<label>label</label>
<label>label</label>
</div>
<>
<label>Tab_1 Tab_1 Tab_1</label>
<br />
<label>Tab_1 Tab_1 Tab_1</label>
<br />
<label>Tab_1 Tab_1 Tab_1</label>
</>
)
},
{
key: "tab2",
title: "Third long tab container",
title: "Second long tab container",
content: (
<div>
<input />
<input />
<input />
</div>
<>
<label>Tab_2 Tab_2 Tab_2</label>
<br />
<label>Tab_2 Tab_2 Tab_2</label>
<br />
<label>Tab_2 Tab_2 Tab_2</label>
</>
)
},
{
key: "tab3",
title: "Short2",
content: (
<div>
<input />
<input />
<input />
</div>
<>
<label>Tab_3 Tab_3 Tab_3</label>
<br />
<label>Tab_3 Tab_3 Tab_3</label>
<br />
<label>Tab_3 Tab_3 Tab_3</label>
</>
)
},
{
key: "tab4",
title: "Third long tab container",
title: "Third long tab container header",
content: (
<div>
<input />
<input />
<input />
</div>
<>
<label>Tab_4 Tab_4 Tab_4</label>
<br />
<label>Tab_4 Tab_4 Tab_4</label>
<br />
<label>Tab_4 Tab_4 Tab_4</label>
</>
)
},
{
key: "tab5",
title: "Short3",
content: (
<div>
<input />
<input />
<input />
</div>
<>
<label>Tab_5 Tab_5 Tab_5</label>
<br />
<label>Tab_5 Tab_5 Tab_5</label>
<br />
<label>Tab_5 Tab_5 Tab_5</label>
</>
)
},
{
key: "tab6",
title: "tab container",
content: (
<>
<label>Tab_6 Tab_6 Tab_6</label>
<br />
<label>Tab_6 Tab_6 Tab_6</label>
<br />
<label>Tab_6 Tab_6 Tab_6</label>
</>
)
},
{
key: "tab7",
title: "Very long tabs-container field",
content: (
<>
<label>Tab_7 Tab_7 Tab_7</label>
<br />
<label>Tab_7 Tab_7 Tab_7</label>
<br />
<label>Tab_7 Tab_7 Tab_7</label>
</>
)
},
{
key: "tab8",
title: "tab container",
content: (
<>
<label>Tab_8 Tab_8 Tab_8</label>
<br />
<label>Tab_8 Tab_8 Tab_8</label>
<br />
<label>Tab_8 Tab_8 Tab_8</label>
</>
)
},
{
key: "tab9",
title: "Short_04",
content: (
<>
<label>Tab_9 Tab_9 Tab_9</label>
<br />
<label>Tab_9 Tab_9 Tab_9</label>
<br />
<label>Tab_9 Tab_9 Tab_9</label>
</>
)
},
{
key: "tab10",
title: "Short__05",
content: (
<>
<label>Tab_10 Tab_10 Tab_10</label>
<br />
<label>Tab_10 Tab_10 Tab_10</label>
<br />
<label>Tab_10 Tab_10 Tab_10</label>
</>
)
},
{
key: "tab11",
title: "TabsContainer",
content: (
<>
<label>Tab_11 Tab_11 Tab_11</label>
<br />
<label>Tab_11 Tab_11 Tab_11</label>
<br />
<label>Tab_11 Tab_11 Tab_11</label>
</>
)
}
];
const tabsItems = [
{
key: "tab0",
title: "Title00000000",
content: (
<>
<label>Tab_0 Tab_0 Tab_0</label>
<br />
<label>Tab_0 Tab_0 Tab_0</label>
<br />
<label>Tab_0 Tab_0 Tab_0</label>
</>
)
},
{
key: "tab1",
title: "Title00000001",
content: (
<>
<label>Tab_1 Tab_1 Tab_1</label>
<br />
<label>Tab_1 Tab_1 Tab_1</label>
<br />
<label>Tab_1 Tab_1 Tab_1</label>
</>
)
},
{
key: "tab2",
title: "Title00000002",
content: (
<>
<label>Tab_2 Tab_2 Tab_2</label>
<br />
<label>Tab_2 Tab_2 Tab_2</label>
<br />
<label>Tab_2 Tab_2 Tab_2</label>
</>
)
},
{
key: "tab3",
title: "Title00000003",
content: (
<>
<label>Tab_3 Tab_3 Tab_3</label>
<br />
<label>Tab_3 Tab_3 Tab_3</label>
<br />
<label>Tab_3 Tab_3 Tab_3</label>
</>
)
},
{
key: "tab4",
title: "Title00000004",
content: (
<>
<label>Tab_4 Tab_4 Tab_4</label>
<br />
<label>Tab_4 Tab_4 Tab_4</label>
<br />
<label>Tab_4 Tab_4 Tab_4</label>
</>
)
},
{
key: "tab5",
title: "Title00000005",
content: (
<>
<label>Tab_5 Tab_5 Tab_5</label>
<br />
<label>Tab_5 Tab_5 Tab_5</label>
<br />
<label>Tab_5 Tab_5 Tab_5</label>
</>
)
},
{
key: "tab6",
title: "Title00000006",
content: (
<>
<label>Tab_6 Tab_6 Tab_6</label>
<br />
<label>Tab_6 Tab_6 Tab_6</label>
<br />
<label>Tab_6 Tab_6 Tab_6</label>
</>
)
},
{
key: "tab7",
title: "Title00000007",
content: (
<>
<label>Tab_7 Tab_7 Tab_7</label>
<br />
<label>Tab_7 Tab_7 Tab_7</label>
<br />
<label>Tab_7 Tab_7 Tab_7</label>
</>
)
},
{
key: "tab8",
title: "Title00000008",
content: (
<>
<label>Tab_8 Tab_8 Tab_8</label>
<br />
<label>Tab_8 Tab_8 Tab_8</label>
<br />
<label>Tab_8 Tab_8 Tab_8</label>
</>
)
},
{
key: "tab9",
title: "Title00000009",
content: (
<>
<label>Tab_9 Tab_9 Tab_9</label>
<br />
<label>Tab_9 Tab_9 Tab_9</label>
<br />
<label>Tab_9 Tab_9 Tab_9</label>
</>
)
}
];
@ -187,12 +410,27 @@ storiesOf("Components|TabContainer", module)
<div style={{ marginTop: 32, maxWidth: 430 }}>
<h5 style={{ marginTop: 100, marginBottom: 20 }}>
TabsContainer with auto scroll:
Autoscrolling with different tab widths:
</h5>
<TabContainer isDisabled={boolean("isDisabled", false)}>
<TabContainer
isDisabled={boolean("isDisabled", false)}
selectedItem={3}
>
{scrollArrayItems}
</TabContainer>
</div>
<div style={{ marginTop: 32, maxWidth: 430 }}>
<h5 style={{ marginTop: 100, marginBottom: 20 }}>
Autoscrolling with the same tabs width:
</h5>
<TabContainer
isDisabled={boolean("isDisabled", false)}
selectedItem={5}
>
{tabsItems}
</TabContainer>
</div>
</Section>
);
});

View File

@ -18,9 +18,11 @@ import { ToggleButton } from "asc-web-components";
#### Properties
| Props | Type | Required | Values | Default | Description |
| ------------ | :------: | :------: | :----: | :-----: | -------------------------------------------------------------- |
| `label` | `string` | - | - | - | Label of the input |
| `isChecked` | `bool` | - | - | - | The checked property sets the checked state of a ToggleButton. |
| `isDisabled` | `bool` | - | - | - | Disables the ToggleButton |
| `onChange` | `func` | ✅ | - | - | Will be triggered whenever an ToggleButton is clicked |
| Props | Type | Required | Values | Default | Description |
| ------------ | :----------------: | :------: | :----: | :-----: | -------------------------------------------------------------- |
| `label` | `string` | - | - | - | Label of the input |
| `isChecked` | `bool` | - | - | - | The checked property sets the checked state of a ToggleButton. |
| `isDisabled` | `bool` | - | - | - | Disables the ToggleButton |
| `onChange` | `func` | ✅ | - | - | Will be triggered whenever an ToggleButton is clicked |
| `className` | `string` | - | - | - | Class name |
| `id` | `string`, `number` | - | - | - | Set component id |

View File

@ -73,7 +73,11 @@ class ToggleButton extends Component {
//console.log("ToggleButton render");
return (
<ToggleButtonContainer id={id} className={className} isDisabled={isDisabled}>
<ToggleButtonContainer
id={id}
className={className}
isDisabled={isDisabled}
>
<HiddenInput
type="checkbox"
checked={this.state.checked}
@ -96,7 +100,7 @@ ToggleButton.propTypes = {
isDisabled: PropTypes.bool,
onChange: PropTypes.func,
label: PropTypes.string,
id: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
className: PropTypes.string
};

View File

@ -8,7 +8,7 @@ import Readme from "./README.md";
import ToggleButton from ".";
import Section from "../../../.storybook/decorators/section";
storiesOf("Components|Input", module)
storiesOf("Components|Buttons", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("toggle button", () => {
@ -17,6 +17,8 @@ storiesOf("Components|Input", module)
<BooleanValue>
{({ value, toggle }) => (
<ToggleButton
id={text("id", "toggle id")}
className={text("className", "toggle className")}
isChecked={value}
isDisabled={boolean("isDisabled", false)}
label={text("label", "label text")}

View File

@ -4542,6 +4542,14 @@ dom-serializer@0:
domelementtype "^2.0.1"
entities "^2.0.0"
dom-serializer@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
dependencies:
domelementtype "^2.0.1"
entities "^2.0.0"
dom-serializer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
@ -4584,6 +4592,13 @@ domhandler@^2.3.0:
dependencies:
domelementtype "1"
domhandler@^3.0, domhandler@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9"
integrity sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==
dependencies:
domelementtype "^2.0.1"
domutils@1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
@ -4600,6 +4615,15 @@ domutils@^1.5.1, domutils@^1.7.0:
dom-serializer "0"
domelementtype "1"
domutils@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08"
integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg==
dependencies:
dom-serializer "^0.2.1"
domelementtype "^2.0.1"
domhandler "^3.0.0"
dot-prop@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
@ -6036,6 +6060,16 @@ html-minifier@^4.0.0:
relateurl "^0.2.7"
uglify-js "^3.5.1"
html-to-react@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/html-to-react/-/html-to-react-1.4.2.tgz#7b628ab56cd63a52f2d0b79d0fa838a51f088a57"
integrity sha512-TdTfxd95sRCo6QL8admCkE7mvNNrXtGoVr1dyS+7uvc8XCqAymnf/6ckclvnVbQNUo2Nh21VPwtfEHd0khiV7g==
dependencies:
domhandler "^3.0"
htmlparser2 "^4.0"
lodash.camelcase "^4.3.0"
ramda "^0.26"
html-webpack-plugin@^4.0.0-beta.2:
version "4.0.0-beta.8"
resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.0.0-beta.8.tgz#d9a8d4322d8cf310f1568f6f4f585a80df0ad378"
@ -6060,6 +6094,16 @@ htmlparser2@^3.3.0, htmlparser2@^3.9.1:
inherits "^2.0.1"
readable-stream "^3.1.1"
htmlparser2@^4.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.0.0.tgz#6034658db65b7713a572a9ebf79f650832dceec8"
integrity sha512-cChwXn5Vam57fyXajDtPXL1wTYc8JtLbr2TN76FYu05itVVVealxLowe2B3IEznJG4p9HAYn/0tJaRlGuEglFQ==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.0.0"
domutils "^2.0.0"
entities "^2.0.0"
http-errors@1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
@ -9412,6 +9456,11 @@ ramda@^0.21.0:
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35"
integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=
ramda@^0.26:
version "0.26.1"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
randexp@0.4.6:
version "0.4.6"
resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"