Merge branch 'master' of https://github.com/ONLYOFFICE/CommunityServer-AspNetCore
This commit is contained in:
commit
b9edf10894
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,7 +3,6 @@
|
||||
*.suo
|
||||
*.user
|
||||
.vs/
|
||||
.vscode/
|
||||
*-lock.json
|
||||
**/node_modules/
|
||||
**/storybook-static/
|
||||
@ -12,3 +11,4 @@
|
||||
*.log
|
||||
/packages/asc-web-components
|
||||
/products/ASC.People/Data/
|
||||
Data/
|
||||
|
@ -0,0 +1,54 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { ComboBox, TextInput } from "asc-web-components";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
margin: 0 0 16px 0;
|
||||
`;
|
||||
|
||||
class ContactField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("ContactField render");
|
||||
|
||||
const {
|
||||
isDisabled,
|
||||
|
||||
comboBoxName,
|
||||
comboBoxOptions,
|
||||
comboBoxSelectedOption,
|
||||
comboBoxOnChange,
|
||||
|
||||
inputName,
|
||||
inputValue,
|
||||
inputOnChange
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ComboBox
|
||||
name={comboBoxName}
|
||||
options={comboBoxOptions}
|
||||
onSelect={comboBoxOnChange}
|
||||
selectedOption={comboBoxSelectedOption}
|
||||
isDisabled={isDisabled}
|
||||
scaled={false}
|
||||
className="field-select"
|
||||
/>
|
||||
<TextInput
|
||||
name={inputName}
|
||||
value={inputValue}
|
||||
isDisabled={isDisabled}
|
||||
onChange={inputOnChange}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ContactField;
|
@ -1,6 +1,8 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components';
|
||||
import { ComboBox, TextInput } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import ContactField from "./ContactField";
|
||||
import { ComboBox } from "asc-web-components";
|
||||
|
||||
const Container = styled.div`
|
||||
width: 100%;
|
||||
@ -12,82 +14,96 @@ const Container = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const Item = styled.div`
|
||||
display: flex;
|
||||
margin: 0 0 16px 0;
|
||||
`;
|
||||
|
||||
const getOptions = (patterns, keyPrefix) => {
|
||||
return patterns.map((item, index) => {
|
||||
return {
|
||||
key: keyPrefix + index,
|
||||
label: item.type, //from resource
|
||||
icon: item.icon,
|
||||
value: item.type
|
||||
};
|
||||
});
|
||||
}
|
||||
return patterns.map((item, index) => {
|
||||
return {
|
||||
key: keyPrefix + index,
|
||||
label: item.type, //from resource
|
||||
icon: item.icon,
|
||||
value: item.type
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const renderItems = (contacts, pattern, onTypeChange, onTextChange, isDisabled) => {
|
||||
const items = contacts.map((contact, index) => {
|
||||
const renderItems = (
|
||||
contacts,
|
||||
pattern,
|
||||
onTypeChange,
|
||||
onTextChange,
|
||||
isDisabled
|
||||
) => {
|
||||
const items = contacts.map((contact, index) => {
|
||||
const prefix = contact.id + "_";
|
||||
const itemOptions = getOptions(pattern, prefix);
|
||||
const itemSelectedOption = itemOptions.filter(
|
||||
option => option.value === contact.type
|
||||
)[0];
|
||||
|
||||
const prefix = contact.id + "_";
|
||||
return (
|
||||
<ContactField
|
||||
key={prefix + "item_" + index}
|
||||
isDisabled={isDisabled}
|
||||
comboBoxName={prefix + "type"}
|
||||
comboBoxOptions={itemOptions}
|
||||
comboBoxSelectedOption={itemSelectedOption}
|
||||
comboBoxOnChange={onTypeChange}
|
||||
inputName={prefix + "value"}
|
||||
inputValue={contact.value}
|
||||
inputOnChange={onTextChange}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const itemOptions = getOptions(pattern, prefix);
|
||||
return items;
|
||||
};
|
||||
|
||||
const itemSelectedOption = itemOptions.filter(option => option.value === contact.type)[0];
|
||||
class ContactsField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
return (
|
||||
<Item key={prefix + "item_" + index}>
|
||||
<ComboBox
|
||||
name={prefix + "type"}
|
||||
options={itemOptions}
|
||||
onSelect={onTypeChange}
|
||||
selectedOption={itemSelectedOption}
|
||||
isDisabled={isDisabled}
|
||||
scaled={false}
|
||||
className="field-select"
|
||||
/>
|
||||
<TextInput
|
||||
name={prefix + "value"}
|
||||
value={contact.value}
|
||||
isDisabled={isDisabled}
|
||||
onChange={onTextChange}
|
||||
/>
|
||||
</Item>
|
||||
);
|
||||
});
|
||||
render() {
|
||||
console.log("ContactsField render");
|
||||
|
||||
return items;
|
||||
};
|
||||
const {
|
||||
pattern,
|
||||
contacts,
|
||||
addItemText,
|
||||
onItemAdd,
|
||||
onItemTypeChange,
|
||||
onItemTextChange,
|
||||
isDisabled
|
||||
} = this.props;
|
||||
|
||||
const ContactsField = React.memo((props) => {
|
||||
|
||||
const { pattern, contacts, addItemText, onItemAdd, onItemTypeChange, onItemTextChange, isDisabled } = props;
|
||||
|
||||
const existItems = renderItems(contacts, pattern, onItemTypeChange, onItemTextChange, isDisabled);
|
||||
const existItems = renderItems(
|
||||
contacts,
|
||||
pattern,
|
||||
onItemTypeChange,
|
||||
onItemTextChange,
|
||||
isDisabled
|
||||
);
|
||||
|
||||
const prefix = "null_";
|
||||
|
||||
const options = getOptions(pattern, prefix);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{existItems}
|
||||
<ComboBox
|
||||
options={options}
|
||||
onSelect={onItemAdd}
|
||||
selectedOption={{
|
||||
key: prefix,
|
||||
label: addItemText,
|
||||
value: ""
|
||||
}}
|
||||
isDisabled={isDisabled}
|
||||
scaled={false}
|
||||
className="field-select"
|
||||
/>
|
||||
</Container>
|
||||
<Container>
|
||||
{existItems}
|
||||
<ComboBox
|
||||
options={options}
|
||||
onSelect={onItemAdd}
|
||||
selectedOption={{
|
||||
key: prefix,
|
||||
label: addItemText,
|
||||
value: ""
|
||||
}}
|
||||
isDisabled={isDisabled}
|
||||
scaled={false}
|
||||
className="field-select"
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default ContactsField
|
||||
export default ContactsField;
|
||||
|
@ -1,35 +1,44 @@
|
||||
import React from 'react'
|
||||
import { FieldContainer, DateInput } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { FieldContainer, DatePicker } from "asc-web-components";
|
||||
|
||||
const DateField = React.memo((props) => {
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
class DateField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
inputName,
|
||||
inputValue,
|
||||
inputIsDisabled,
|
||||
inputOnChange,
|
||||
inputTabIndex
|
||||
} = props;
|
||||
render() {
|
||||
console.log("DateField render");
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<DateInput
|
||||
name={inputName}
|
||||
selected={inputValue}
|
||||
disabled={inputIsDisabled}
|
||||
onChange={inputOnChange}
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
|
||||
inputName,
|
||||
inputValue,
|
||||
inputIsDisabled,
|
||||
inputOnChange,
|
||||
inputTabIndex
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
tabIndex={inputTabIndex}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
labelText={labelText}
|
||||
>
|
||||
<DatePicker
|
||||
name={inputName}
|
||||
selectedDate={inputValue}
|
||||
disabled={inputIsDisabled}
|
||||
onChange={inputOnChange}
|
||||
hasError={hasError}
|
||||
tabIndex={inputTabIndex}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DateField
|
||||
export default DateField;
|
||||
|
@ -1,42 +1,57 @@
|
||||
import React from 'react'
|
||||
import { FieldContainer, SelectorAddButton, SelectedItem } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import {
|
||||
FieldContainer,
|
||||
SelectorAddButton,
|
||||
SelectedItem
|
||||
} from "asc-web-components";
|
||||
|
||||
const DepartmentField = React.memo((props) => {
|
||||
const {
|
||||
isRequired,
|
||||
isDisabled,
|
||||
hasError,
|
||||
labelText,
|
||||
addButtonTitle,
|
||||
departments,
|
||||
onAddDepartment,
|
||||
onRemoveDepartment
|
||||
} = props;
|
||||
class DepartmentField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
className="departments-field"
|
||||
>
|
||||
<SelectorAddButton
|
||||
isDisabled={isDisabled}
|
||||
title={addButtonTitle}
|
||||
onClick={onAddDepartment}
|
||||
className="department-add-btn"
|
||||
/>
|
||||
{departments && departments.map((department) => (
|
||||
<SelectedItem
|
||||
key={`department_${department.id}`}
|
||||
text={department.name}
|
||||
onClose={() => { onRemoveDepartment(department.id) }}
|
||||
isInline={true}
|
||||
className="department-item"
|
||||
render() {
|
||||
console.log("DepartmentField render");
|
||||
|
||||
const {
|
||||
isRequired,
|
||||
isDisabled,
|
||||
hasError,
|
||||
labelText,
|
||||
addButtonTitle,
|
||||
departments,
|
||||
onAddDepartment,
|
||||
onRemoveDepartment
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
className="departments-field"
|
||||
>
|
||||
<SelectorAddButton
|
||||
isDisabled={isDisabled}
|
||||
title={addButtonTitle}
|
||||
onClick={onAddDepartment}
|
||||
className="department-add-btn"
|
||||
/>
|
||||
))}
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
{departments.map(department => (
|
||||
<SelectedItem
|
||||
key={`department_${department.id}`}
|
||||
text={department.name}
|
||||
onClose={() => {
|
||||
onRemoveDepartment(department.id);
|
||||
}}
|
||||
isInline={true}
|
||||
className="department-item"
|
||||
/>
|
||||
))}
|
||||
</FieldContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DepartmentField
|
||||
export default DepartmentField;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components';
|
||||
import { Text } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import { Text } from "asc-web-components";
|
||||
|
||||
const Container = styled.div`
|
||||
margin: 0 0 40px 0;
|
||||
@ -11,7 +11,7 @@ const Header = styled(Text.ContentHeader)`
|
||||
line-height: unset;
|
||||
`;
|
||||
|
||||
const InfoFieldContainer = React.memo((props) => {
|
||||
const InfoFieldContainer = React.memo(props => {
|
||||
const { headerText, children } = props;
|
||||
|
||||
return (
|
||||
@ -22,4 +22,4 @@ const InfoFieldContainer = React.memo((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
export default InfoFieldContainer
|
||||
export default InfoFieldContainer;
|
||||
|
@ -1,66 +1,80 @@
|
||||
import React from 'react'
|
||||
import { FieldContainer, RadioButtonGroup, PasswordInput } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import {
|
||||
FieldContainer,
|
||||
RadioButtonGroup,
|
||||
PasswordInput
|
||||
} from "asc-web-components";
|
||||
|
||||
const PasswordField = React.memo((props) => {
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
passwordSettings,
|
||||
class PasswordField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
radioName,
|
||||
radioValue,
|
||||
radioOptions,
|
||||
radioIsDisabled,
|
||||
radioOnChange,
|
||||
render() {
|
||||
console.log("PasswordField render");
|
||||
|
||||
inputName,
|
||||
emailInputName,
|
||||
inputValue,
|
||||
inputIsDisabled,
|
||||
inputOnChange,
|
||||
inputTabIndex,
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
passwordSettings,
|
||||
|
||||
copyLinkText,
|
||||
} = props;
|
||||
radioName,
|
||||
radioValue,
|
||||
radioOptions,
|
||||
radioIsDisabled,
|
||||
radioOnChange,
|
||||
|
||||
const tooltipPasswordLength = 'from ' + passwordSettings.minLength + ' to 30 characters';
|
||||
inputName,
|
||||
emailInputName,
|
||||
inputValue,
|
||||
inputIsDisabled,
|
||||
inputOnChange,
|
||||
inputTabIndex,
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<RadioButtonGroup
|
||||
name={radioName}
|
||||
selected={radioValue}
|
||||
options={radioOptions}
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
/>
|
||||
<PasswordInput
|
||||
inputName={inputName}
|
||||
emailInputName={emailInputName}
|
||||
inputValue={inputValue}
|
||||
inputWidth="320px"
|
||||
inputTabIndex={inputTabIndex}
|
||||
onChange={inputOnChange}
|
||||
clipActionResource={copyLinkText}
|
||||
clipEmailResource='E-mail: '
|
||||
clipPasswordResource='Password: '
|
||||
tooltipPasswordTitle='Password must contain:'
|
||||
tooltipPasswordLength={tooltipPasswordLength}
|
||||
tooltipPasswordDigits='digits'
|
||||
tooltipPasswordCapital='capital letters'
|
||||
tooltipPasswordSpecial='special characters (!@#$%^&*)'
|
||||
generatorSpecial='!@#$%^&*'
|
||||
passwordSettings={passwordSettings}
|
||||
isDisabled={inputIsDisabled}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
copyLinkText
|
||||
} = this.props;
|
||||
|
||||
const tooltipPasswordLength =
|
||||
"from " + passwordSettings.minLength + " to 30 characters";
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<RadioButtonGroup
|
||||
name={radioName}
|
||||
selected={radioValue}
|
||||
options={radioOptions}
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
/>
|
||||
<PasswordInput
|
||||
inputName={inputName}
|
||||
emailInputName={emailInputName}
|
||||
inputValue={inputValue}
|
||||
inputWidth="320px"
|
||||
inputTabIndex={inputTabIndex}
|
||||
onChange={inputOnChange}
|
||||
clipActionResource={copyLinkText}
|
||||
clipEmailResource="E-mail: "
|
||||
clipPasswordResource="Password: "
|
||||
tooltipPasswordTitle="Password must contain:"
|
||||
tooltipPasswordLength={tooltipPasswordLength}
|
||||
tooltipPasswordDigits="digits"
|
||||
tooltipPasswordCapital="capital letters"
|
||||
tooltipPasswordSpecial="special characters (!@#$%^&*)"
|
||||
generatorSpecial="!@#$%^&*"
|
||||
passwordSettings={passwordSettings}
|
||||
isDisabled={inputIsDisabled}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PasswordField;
|
@ -1,35 +1,44 @@
|
||||
import React from 'react'
|
||||
import { FieldContainer, RadioButtonGroup } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { FieldContainer, RadioButtonGroup } from "asc-web-components";
|
||||
|
||||
const RadioField = React.memo((props) => {
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
class RadioField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
radioName,
|
||||
radioValue,
|
||||
radioOptions,
|
||||
radioIsDisabled,
|
||||
radioOnChange
|
||||
} = props;
|
||||
render() {
|
||||
console.log("RadioField render");
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<RadioButtonGroup
|
||||
name={radioName}
|
||||
selected={radioValue}
|
||||
options={radioOptions}
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
|
||||
export default RadioField
|
||||
radioName,
|
||||
radioValue,
|
||||
radioOptions,
|
||||
radioIsDisabled,
|
||||
radioOnChange
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<RadioButtonGroup
|
||||
name={radioName}
|
||||
selected={radioValue}
|
||||
options={radioOptions}
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RadioField;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components';
|
||||
import { FieldContainer, TextInput, Button } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { FieldContainer, TextInput, Button } from "asc-web-components";
|
||||
|
||||
const InputContainer = styled.div`
|
||||
width: 100%;
|
||||
@ -9,47 +10,55 @@ const InputContainer = styled.div`
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const TextChangeField = React.memo((props) => {
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
class TextChangeField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
inputName,
|
||||
inputValue,
|
||||
inputTabIndex,
|
||||
render() {
|
||||
console.log("TextChangeField render");
|
||||
|
||||
buttonText,
|
||||
buttonIsDisabled,
|
||||
buttonOnClick,
|
||||
buttonTabIndex
|
||||
} = props;
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<InputContainer>
|
||||
<TextInput
|
||||
name={inputName}
|
||||
value={inputValue}
|
||||
isDisabled={true}
|
||||
hasError={hasError}
|
||||
tabIndex={inputTabIndex}
|
||||
/>
|
||||
<Button
|
||||
label={buttonText}
|
||||
onClick={buttonOnClick}
|
||||
isDisabled={buttonIsDisabled}
|
||||
size="medium"
|
||||
style={{ marginLeft: "8px" }}
|
||||
tabIndex={buttonTabIndex}
|
||||
/>
|
||||
</InputContainer>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
inputName,
|
||||
inputValue,
|
||||
inputTabIndex,
|
||||
|
||||
buttonText,
|
||||
buttonIsDisabled,
|
||||
buttonOnClick,
|
||||
buttonTabIndex
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<InputContainer>
|
||||
<TextInput
|
||||
name={inputName}
|
||||
value={inputValue}
|
||||
isDisabled={true}
|
||||
hasError={hasError}
|
||||
tabIndex={inputTabIndex}
|
||||
/>
|
||||
<Button
|
||||
label={buttonText}
|
||||
onClick={buttonOnClick}
|
||||
isDisabled={buttonIsDisabled}
|
||||
size="medium"
|
||||
style={{ marginLeft: "8px" }}
|
||||
tabIndex={buttonTabIndex}
|
||||
/>
|
||||
</InputContainer>
|
||||
</FieldContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TextChangeField;
|
@ -1,38 +1,47 @@
|
||||
import React from 'react'
|
||||
import { FieldContainer, TextInput } from 'asc-web-components'
|
||||
import React from "react";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { FieldContainer, TextInput } from "asc-web-components";
|
||||
|
||||
const TextField = React.memo((props) => {
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
class TextField extends React.Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
inputName,
|
||||
inputValue,
|
||||
inputIsDisabled,
|
||||
inputOnChange,
|
||||
inputAutoFocussed,
|
||||
inputTabIndex
|
||||
} = props;
|
||||
render() {
|
||||
console.log("TextField render");
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
>
|
||||
<TextInput
|
||||
name={inputName}
|
||||
value={inputValue}
|
||||
isDisabled={inputIsDisabled}
|
||||
onChange={inputOnChange}
|
||||
const {
|
||||
isRequired,
|
||||
hasError,
|
||||
labelText,
|
||||
|
||||
inputName,
|
||||
inputValue,
|
||||
inputIsDisabled,
|
||||
inputOnChange,
|
||||
inputAutoFocussed,
|
||||
inputTabIndex
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<FieldContainer
|
||||
isRequired={isRequired}
|
||||
hasError={hasError}
|
||||
className="field-input"
|
||||
isAutoFocussed={inputAutoFocussed}
|
||||
tabIndex={inputTabIndex}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
});
|
||||
labelText={labelText}
|
||||
>
|
||||
<TextInput
|
||||
name={inputName}
|
||||
value={inputValue}
|
||||
isDisabled={inputIsDisabled}
|
||||
onChange={inputOnChange}
|
||||
hasError={hasError}
|
||||
className="field-input"
|
||||
isAutoFocussed={inputAutoFocussed}
|
||||
tabIndex={inputTabIndex}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default TextField;
|
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { withRouter } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { Avatar, Button, Textarea, Text, toastr } from 'asc-web-components'
|
||||
import { Avatar, Button, Textarea, toastr } from 'asc-web-components'
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { toEmployeeWrapper, getUserRole, getUserContactsPattern, getUserContacts } from "../../../../../store/people/selectors";
|
||||
import { createProfile } from '../../../../../store/profile/actions';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { find, filter } from "lodash";
|
||||
import { find, filter, cloneDeep } from "lodash";
|
||||
import { EmployeeActivationStatus, EmployeeStatus } from "../../helpers/constants";
|
||||
|
||||
export function getSelectedUser(selection, userId) {
|
||||
@ -141,5 +141,5 @@ export function toEmployeeWrapper(profile) {
|
||||
contacts: []
|
||||
};
|
||||
|
||||
return { ...emptyData, ...profile };
|
||||
return cloneDeep({ ...emptyData, ...profile });
|
||||
}
|
@ -318,6 +318,10 @@ namespace ASC.Employee.Core.Controllers
|
||||
[Authorize(AuthenticationSchemes = "confirm")]
|
||||
public EmployeeWraperFull AddMember(MemberModel memberModel)
|
||||
{
|
||||
if (HttpContext.User.IsInRole(ASC.Common.Security.Authorizing.Role.System))
|
||||
{
|
||||
SecurityContext.AuthenticateMe(ASC.Core.Configuration.Constants.CoreSystem);
|
||||
}
|
||||
SecurityContext.DemandPermissions(Tenant, Constants.Action_AddRemoveUser);
|
||||
|
||||
if (string.IsNullOrEmpty(memberModel.Password))
|
||||
|
@ -60,8 +60,6 @@ namespace ASC.People
|
||||
|
||||
var builder = services.AddMvc(config =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
|
||||
config.Filters.Add(new AuthorizeFilter(policy));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(TenantStatusFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(PaymentFilter)));
|
||||
config.Filters.Add(new TypeFilterAttribute(typeof(IpSecurityFilter)));
|
||||
|
@ -4,6 +4,7 @@ import { Loader } from "asc-web-components";
|
||||
import StudioLayout from "./components/Layout/index";
|
||||
import Login from "./components/pages/Login";
|
||||
import { PrivateRoute } from "./helpers/privateRoute";
|
||||
import { PublicRoute } from "./helpers/publicRoute";
|
||||
import { Error404 } from "./components/pages/Error";
|
||||
|
||||
const Home = lazy(() => import("./components/pages/Home"));
|
||||
@ -18,8 +19,8 @@ const App = () => {
|
||||
fallback={<Loader className="pageLoader" type="rombs" size={40} />}
|
||||
>
|
||||
<Switch>
|
||||
<Route exact path="/login" component={Login} />
|
||||
<Route exact path="/confirm" component={Confirm} />
|
||||
<PublicRoute exact path="/login" component={Login} />
|
||||
<PublicRoute path="/confirm" component={Confirm} />
|
||||
<PrivateRoute exact path="/" component={Home} />
|
||||
<PrivateRoute exact path="/about" component={About} />
|
||||
<PrivateRoute component={Error404} />
|
||||
|
24
web/ASC.Web.Client/src/helpers/publicRoute.js
Normal file
24
web/ASC.Web.Client/src/helpers/publicRoute.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { Redirect, Route } from 'react-router-dom';
|
||||
import { AUTH_KEY } from './constants';
|
||||
import Cookies from 'universal-cookie';
|
||||
|
||||
export const PublicRoute = ({ component: Component, ...rest }) => {
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={props =>
|
||||
(new Cookies()).get(AUTH_KEY) ? (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/",
|
||||
state: { from: props.location }
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Component {...props} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
};
|
37
web/ASC.Web.Components/.vscode/launch.json
vendored
Normal file
37
web/ASC.Web.Components/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Jest All",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||
"args": [
|
||||
"--runInBand"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true,
|
||||
"windows": {
|
||||
"program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Jest Current File",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||||
"args": [
|
||||
"${fileBasenameNoExtension}",
|
||||
"--config",
|
||||
"jest.config.js"
|
||||
],
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true,
|
||||
"windows": {
|
||||
"program": "${workspaceFolder}/node_modules/jest-cli/bin/jest",
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-components",
|
||||
"version": "1.0.63",
|
||||
"version": "1.0.66",
|
||||
"description": "Ascensio System SIA component library",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/asc-web-components.js",
|
||||
|
@ -6,7 +6,7 @@ import withReadme from "storybook-readme/with-readme";
|
||||
import Readme from "./README.md";
|
||||
import AdvancedSelector from "./";
|
||||
import Section from "../../../.storybook/decorators/section";
|
||||
import { boolean } from "@storybook/addon-knobs/dist/deprecated";
|
||||
import { boolean, select } from "@storybook/addon-knobs/dist/deprecated";
|
||||
import { ArrayValue, BooleanValue } from "react-values";
|
||||
import Button from "../button";
|
||||
|
||||
@ -77,6 +77,7 @@ storiesOf("Components|AdvancedSelector", module)
|
||||
>
|
||||
{({ value, set }) => (
|
||||
<AdvancedSelector
|
||||
size={select("size", ["compact", "full"], "compact")}
|
||||
placeholder={text("placeholder", "Search users")}
|
||||
onSearchChanged={value => {
|
||||
action("onSearchChanged")(value);
|
||||
@ -179,6 +180,7 @@ storiesOf("Components|AdvancedSelector", module)
|
||||
>
|
||||
{({ value, set }) => (
|
||||
<AdvancedSelector
|
||||
size={select("size", ["compact", "full"], "compact")}
|
||||
isDropDown={true}
|
||||
isOpen={isOpen}
|
||||
placeholder={text("placeholder", "Search users")}
|
||||
|
@ -12,6 +12,8 @@ import { isArrayEqual } from "../../utils/array";
|
||||
import findIndex from "lodash/findIndex";
|
||||
import filter from "lodash/filter";
|
||||
import DropDown from "../drop-down";
|
||||
import { handleAnyClick } from "../../utils/event";
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react/prop-types */
|
||||
@ -19,7 +21,7 @@ const Container = ({
|
||||
value,
|
||||
placeholder,
|
||||
isMultiSelect,
|
||||
mode,
|
||||
size,
|
||||
width,
|
||||
maxHeight,
|
||||
isDisabled,
|
||||
@ -32,16 +34,27 @@ const Container = ({
|
||||
groups,
|
||||
selectedGroups,
|
||||
onChangeGroup,
|
||||
isOpen,
|
||||
isDropDown,
|
||||
containerWidth,
|
||||
containerHeight,
|
||||
...props
|
||||
}) => <div {...props} />;
|
||||
/* eslint-enable react/prop-types */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
const StyledContainer = styled(Container)`
|
||||
${props => (props.width ? `width: ${props.width}px;` : "")}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
${props => (props.containerWidth ? `width: ${props.containerWidth}px;` : "")}
|
||||
${props =>
|
||||
props.containerHeight
|
||||
? `height: ${props.containerHeight}px;`
|
||||
: ""}
|
||||
|
||||
.data_container {
|
||||
margin: 16px;
|
||||
margin: 16px 16px 0 16px;
|
||||
|
||||
.options_searcher {
|
||||
margin-bottom: 12px;
|
||||
@ -53,7 +66,7 @@ const StyledContainer = styled(Container)`
|
||||
|
||||
.option_select_all_checkbox {
|
||||
margin-bottom: 12px;
|
||||
margin-left: 8px;
|
||||
/*margin-left: 8px;*/
|
||||
}
|
||||
|
||||
.options_list {
|
||||
@ -62,33 +75,36 @@ const StyledContainer = styled(Container)`
|
||||
cursor: pointer;
|
||||
|
||||
.option_checkbox {
|
||||
margin-left: 8px;
|
||||
/*margin-left: 8px;*/
|
||||
}
|
||||
|
||||
.option_link {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
/*&:hover {
|
||||
background-color: #eceef1;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button_container {
|
||||
border-top: 1px solid #eceef1;
|
||||
.add_members_btn {
|
||||
margin: 16px;
|
||||
width: 293px;
|
||||
border-top: 1px solid #eceef1;
|
||||
display: flex;
|
||||
|
||||
.add_members_btn {
|
||||
margin: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class AdvancedSelector extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.ref = React.createRef();
|
||||
|
||||
const groups = this.convertGroups(this.props.groups);
|
||||
const currentGroup = this.getCurrentGroup(groups);
|
||||
|
||||
@ -98,25 +114,53 @@ class AdvancedSelector extends React.Component {
|
||||
groups: groups,
|
||||
currentGroup: currentGroup
|
||||
};
|
||||
|
||||
if (props.isOpen) handleAnyClick(true, this.handleClick);
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
if (this.props.isOpen && !this.ref.current.contains(e.target)) {
|
||||
this.props.onSelect && this.props.onSelect(this.state.selectedOptions);
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
handleAnyClick(false, this.handleClick);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
let newState = {};
|
||||
|
||||
if (!isArrayEqual(this.props.selectedOptions, prevProps.selectedOptions)) {
|
||||
this.setState({ selectedOptions: this.props.selectedOptions });
|
||||
newState = { selectedOptions: this.props.selectedOptions };
|
||||
}
|
||||
|
||||
if (this.props.isMultiSelect !== prevProps.isMultiSelect) {
|
||||
this.setState({ selectedOptions: [] });
|
||||
newState = Object.assign({}, newState, {
|
||||
selectedOptions: []
|
||||
});
|
||||
}
|
||||
|
||||
if (this.props.selectedAll !== prevProps.selectedAll) {
|
||||
this.setState({ selectedAll: this.props.selectedAll });
|
||||
newState = Object.assign({}, newState, {
|
||||
selectedAll: this.props.selectedAll
|
||||
});
|
||||
}
|
||||
|
||||
if (!isArrayEqual(this.props.groups, prevProps.groups)) {
|
||||
const groups = this.convertGroups(this.props.groups);
|
||||
const currentGroup = this.getCurrentGroup(groups);
|
||||
this.setState({ groups, currentGroup });
|
||||
newState = Object.assign({}, newState, {
|
||||
groups, currentGroup
|
||||
});
|
||||
}
|
||||
|
||||
if(!isEmpty(newState)) {
|
||||
this.setState({ ...this.state, ...newState });
|
||||
}
|
||||
|
||||
if (this.props.isOpen !== prevProps.isOpen) {
|
||||
handleAnyClick(this.props.isOpen, this.handleClick);
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,19 +259,29 @@ class AdvancedSelector extends React.Component {
|
||||
const {
|
||||
value,
|
||||
placeholder,
|
||||
maxHeight,
|
||||
isDisabled,
|
||||
onSearchChanged,
|
||||
options,
|
||||
isMultiSelect,
|
||||
buttonLabel,
|
||||
selectAllLabel
|
||||
selectAllLabel,
|
||||
size
|
||||
} = this.props;
|
||||
|
||||
const { selectedOptions, selectedAll, currentGroup, groups } = this.state;
|
||||
|
||||
const containerHeight = size === "compact" ? (!groups || !groups.length ? 336 : 326) : 545;
|
||||
const containerWidth = size === "compact" ? (!groups || !groups.length ? 325 : 326) : 690;
|
||||
const listHeight = size === "compact" ? (!groups || !groups.length ? 176 : 120) : 345;
|
||||
const itemHeight = 32;
|
||||
|
||||
return (
|
||||
<StyledContainer {...this.props}>
|
||||
<div className="data_container">
|
||||
<StyledContainer
|
||||
containerHeight={containerHeight}
|
||||
containerWidth={containerWidth}
|
||||
{...this.props}
|
||||
>
|
||||
<div className="data_container" ref={this.ref}>
|
||||
<SearchInput
|
||||
className="options_searcher"
|
||||
isDisabled={isDisabled}
|
||||
@ -264,10 +318,10 @@ class AdvancedSelector extends React.Component {
|
||||
)}
|
||||
<FixedSizeList
|
||||
className="options_list"
|
||||
height={maxHeight}
|
||||
itemSize={32}
|
||||
itemCount={options.length}
|
||||
itemData={options}
|
||||
height={listHeight}
|
||||
itemSize={itemHeight}
|
||||
itemCount={this.props.options.length}
|
||||
itemData={this.props.options}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{this.renderRow.bind(this)}
|
||||
@ -310,8 +364,7 @@ AdvancedSelector.propTypes = {
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
isMultiSelect: PropTypes.bool,
|
||||
mode: PropTypes.oneOf(["base", "compact"]),
|
||||
width: PropTypes.number,
|
||||
size: PropTypes.oneOf(["compact", "full"]),
|
||||
maxHeight: PropTypes.number,
|
||||
isDisabled: PropTypes.bool,
|
||||
onSearchChanged: PropTypes.func,
|
||||
@ -330,9 +383,7 @@ AdvancedSelector.propTypes = {
|
||||
|
||||
AdvancedSelector.defaultProps = {
|
||||
isMultiSelect: false,
|
||||
width: 325,
|
||||
maxHeight: 545,
|
||||
mode: "base",
|
||||
size: "compact",
|
||||
buttonLabel: "Add members",
|
||||
selectAllLabel: "Select all"
|
||||
};
|
||||
|
@ -0,0 +1,98 @@
|
||||
import React from "react";
|
||||
import { storiesOf } from "@storybook/react";
|
||||
import { action } from "@storybook/addon-actions";
|
||||
import { withKnobs, text } from "@storybook/addon-knobs/react";
|
||||
import AdvancedSelector from "../advanced-selector";
|
||||
import Section from "../../../.storybook/decorators/section";
|
||||
import { boolean } from "@storybook/addon-knobs/dist/deprecated";
|
||||
import { ArrayValue, BooleanValue } from "react-values";
|
||||
import Button from "../button";
|
||||
|
||||
storiesOf("EXAMPLES|AdvancedSelector", module)
|
||||
.addDecorator(withKnobs)
|
||||
// To set a default viewport for all the stories for this component
|
||||
.addParameters({ viewport: { defaultViewport: "responsive" } })
|
||||
.add("people group selector", () => {
|
||||
const options = [
|
||||
{
|
||||
key: "group-all",
|
||||
label: "All groups",
|
||||
total: 0
|
||||
},
|
||||
{
|
||||
key: "group-dev",
|
||||
label: "Development",
|
||||
total: 0
|
||||
},
|
||||
{
|
||||
key: "group-management",
|
||||
label: "Management",
|
||||
total: 0
|
||||
},
|
||||
{
|
||||
key: "group-marketing",
|
||||
label: "Marketing",
|
||||
total: 0
|
||||
},
|
||||
{
|
||||
key: "group-mobile",
|
||||
label: "Mobile",
|
||||
total: 0
|
||||
},
|
||||
{
|
||||
key: "group-support",
|
||||
label: "Support",
|
||||
total: 0
|
||||
},
|
||||
{
|
||||
key: "group-web",
|
||||
label: "Web",
|
||||
total: 0
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<BooleanValue
|
||||
defaultValue={true}
|
||||
onChange={() => action("isOpen changed")}
|
||||
>
|
||||
{({ value: isOpen, toggle }) => (
|
||||
<div style={{ position: "relative" }}>
|
||||
<Button label="Toggle dropdown" onClick={toggle} />
|
||||
<ArrayValue
|
||||
defaultValue={options}
|
||||
onChange={() => action("options onChange")}
|
||||
>
|
||||
{({ value, set }) => (
|
||||
<AdvancedSelector
|
||||
isDropDown={true}
|
||||
isOpen={isOpen}
|
||||
maxHeight={336}
|
||||
width={379}
|
||||
placeholder={text("placeholder", "Search")}
|
||||
onSearchChanged={value => {
|
||||
action("onSearchChanged")(value);
|
||||
set(
|
||||
options.filter(option => {
|
||||
return option.label.indexOf(value) > -1;
|
||||
})
|
||||
);
|
||||
}}
|
||||
options={value}
|
||||
isMultiSelect={boolean("isMultiSelect", true)}
|
||||
buttonLabel={text("buttonLabel", "Add departments")}
|
||||
selectAllLabel={text("selectAllLabel", "Select all")}
|
||||
onSelect={selectedOptions => {
|
||||
action("onSelect")(selectedOptions);
|
||||
toggle();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ArrayValue>
|
||||
</div>
|
||||
)}
|
||||
</BooleanValue>
|
||||
</Section>
|
||||
);
|
||||
});
|
@ -2,13 +2,14 @@ import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from 'styled-components';
|
||||
import { Icons } from '../icons';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
const StyledOuter = styled.div`
|
||||
width: ${props => props.size ? Math.abs(parseInt(props.size)) + "px" : "20px"};
|
||||
cursor: ${props => props.isDisabled || !props.isClickable ? 'default' : 'pointer'};
|
||||
line-height: 0;
|
||||
`;
|
||||
class IconButton extends React.Component{
|
||||
class IconButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@ -25,8 +26,8 @@ class IconButton extends React.Component{
|
||||
}
|
||||
|
||||
|
||||
onMouseEnter(e){
|
||||
if(!this.props.isDisabled){
|
||||
onMouseEnter(e) {
|
||||
if (!this.props.isDisabled) {
|
||||
this.setState({
|
||||
currentIconName: this.props.iconHoverName ? this.props.iconHoverName : this.props.iconName,
|
||||
currentIconColor: this.props.hoverColor ? this.props.hoverColor : this.props.color
|
||||
@ -34,8 +35,8 @@ class IconButton extends React.Component{
|
||||
this.props.onMouseEnter && this.props.onMouseEnter(e);
|
||||
}
|
||||
}
|
||||
onMouseLeave(e){
|
||||
if(!this.props.isDisabled){
|
||||
onMouseLeave(e) {
|
||||
if (!this.props.isDisabled) {
|
||||
this.setState({
|
||||
currentIconName: this.props.iconName,
|
||||
currentIconColor: this.props.color
|
||||
@ -43,22 +44,22 @@ class IconButton extends React.Component{
|
||||
this.props.onMouseDown && this.props.onMouseDown(e);
|
||||
}
|
||||
}
|
||||
onMouseDown(e){
|
||||
if(!this.props.isDisabled){
|
||||
onMouseDown(e) {
|
||||
if (!this.props.isDisabled) {
|
||||
this.setState({
|
||||
currentIconName: this.props.iconClickName ? this.props.iconClickName : this.props.iconName,
|
||||
currentIconColor: this.props.clickColor ? this.props.clickColor : this.props.color
|
||||
currentIconColor: this.props.clickColor ? this.props.clickColor : this.props.color
|
||||
});
|
||||
this.props.onMouseDown && this.props.onMouseDown(e);
|
||||
}
|
||||
}
|
||||
onMouseUp(e){
|
||||
if(!this.props.isDisabled){
|
||||
onMouseUp(e) {
|
||||
if (!this.props.isDisabled) {
|
||||
switch (e.nativeEvent.which) {
|
||||
case 1: //Left click
|
||||
this.setState({
|
||||
currentIconName: this.props.iconHoverName ? this.props.iconHoverName : this.props.iconName,
|
||||
currentIconColor: this.props.iconHoverName ? this.props.iconHoverName : this.props.color
|
||||
currentIconColor: this.props.iconHoverName ? this.props.iconHoverName : this.props.color
|
||||
});
|
||||
this.props.onClick && this.props.onClick(e);
|
||||
this.props.onMouseUp && this.props.onMouseUp(e);
|
||||
@ -69,35 +70,24 @@ class IconButton extends React.Component{
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState){
|
||||
if(!this.isNeedUpdate){
|
||||
for (let propsKey in this.props) {
|
||||
if(typeof this.props[propsKey] != "function" && typeof this.props[propsKey] != "object" && this.props[propsKey] != nextProps[propsKey]){
|
||||
this.isNeedUpdate = true;
|
||||
if(propsKey == "iconName"){
|
||||
this.setState({
|
||||
currentIconName: nextProps[propsKey]
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
||||
if (!isEqual(this.props, nextProps)) {
|
||||
let newState = {
|
||||
currentIconName: this.state.currentIconName,
|
||||
currentIconColor: this.state.currentIconColor
|
||||
}
|
||||
for (let stateKey in this.state) {
|
||||
if(typeof this.state[stateKey] != "function" && typeof this.state[stateKey] != "object" && this.state[stateKey] != nextState[stateKey]){
|
||||
this.isNeedUpdate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!this.isNeedUpdate) return false;
|
||||
else return true;
|
||||
if (this.props.iconName !== nextProps.iconName) newState.currentIconName = nextProps.iconName;
|
||||
if (this.props.color !== nextProps.color) newState.currentIconColor = nextProps.color;
|
||||
this.setState(newState);
|
||||
return true;
|
||||
}
|
||||
this.isNeedUpdate = false;
|
||||
return true;
|
||||
return !isEqual(this.state, nextState);
|
||||
}
|
||||
render(){
|
||||
render() {
|
||||
//console.log("IconButton render");
|
||||
return (
|
||||
<StyledOuter
|
||||
@ -111,7 +101,7 @@ class IconButton extends React.Component{
|
||||
|
||||
isClickable={typeof this.props.onClick === 'function'}
|
||||
>
|
||||
{React.createElement(Icons[this.state.currentIconName], {size: "scale", color: this.state.currentIconColor, isfill: this.props.isFill})}
|
||||
{React.createElement(Icons[this.state.currentIconName], { size: "scale", color: this.state.currentIconColor, isfill: this.props.isFill })}
|
||||
</StyledOuter>
|
||||
);
|
||||
}
|
||||
@ -127,7 +117,7 @@ IconButton.propTypes = {
|
||||
iconName: PropTypes.string.isRequired,
|
||||
iconHoverName: PropTypes.string,
|
||||
iconClickName: PropTypes.string,
|
||||
onClick:PropTypes.func
|
||||
onClick: PropTypes.func
|
||||
};
|
||||
|
||||
IconButton.defaultProps = {
|
||||
|
@ -273,6 +273,7 @@ class PasswordInput extends React.PureComponent {
|
||||
} = this.state;
|
||||
|
||||
const iconsColor = isDisabled ? '#D0D5DA' : '#A3A9AE';
|
||||
const iconName = type === 'password' ? 'EyeIcon' : 'EyeOffIcon';
|
||||
|
||||
const tooltipContent = (
|
||||
<StyledTooltipContainer forwardedAs='div' title={tooltipPasswordTitle}>
|
||||
@ -305,7 +306,7 @@ class PasswordInput extends React.PureComponent {
|
||||
name={inputName}
|
||||
hasError={hasError}
|
||||
isDisabled={isDisabled}
|
||||
iconName='EyeIcon'
|
||||
iconName={iconName}
|
||||
value={inputValue}
|
||||
onIconClick={this.changeInputType}
|
||||
onChange={this.onChangeAction}
|
||||
|
@ -2,40 +2,98 @@ import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import PasswordInput from '.';
|
||||
|
||||
const basePasswordSettings = {
|
||||
minLength: 6,
|
||||
upperCase: false,
|
||||
digits: false,
|
||||
specSymbols: false
|
||||
};
|
||||
|
||||
const baseProps = {
|
||||
inputName: 'demoPasswordInput',
|
||||
emailInputName: 'demoEmailInput',
|
||||
inputValue: '',
|
||||
clipActionResource: 'Copy e-mail and password',
|
||||
clipEmailResource: 'E-mail: ',
|
||||
clipPasswordResource: 'Password: ',
|
||||
tooltipPasswordTitle: 'Password must contain:',
|
||||
tooltipPasswordLength: 'from 6 to 30 characters',
|
||||
tooltipPasswordDigits: 'digits',
|
||||
tooltipPasswordCapital: 'capital letters',
|
||||
tooltipPasswordSpecial: 'special characters (!@#$%^&*)',
|
||||
generatorSpecial: '!@#$%^&*',
|
||||
passwordSettings: basePasswordSettings,
|
||||
isDisabled: false,
|
||||
placeholder: 'password',
|
||||
onChange: () => jest.fn(),
|
||||
onValidateInput: () => jest.fn(),
|
||||
onCopyToClipboard: () => jest.fn()
|
||||
}
|
||||
|
||||
describe('<PasswordInput />', () => {
|
||||
it('renders without error', () => {
|
||||
const settings = {
|
||||
minLength: 6,
|
||||
upperCase: false,
|
||||
digits: false,
|
||||
specSymbols: false
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
<PasswordInput
|
||||
inputName="demoPasswordInput"
|
||||
emailInputName="demoEmailInput"
|
||||
inputValue={""}
|
||||
onChange={e => {
|
||||
console.log(e.target.value);
|
||||
}}
|
||||
clipActionResource="Copy e-mail and password"
|
||||
clipEmailResource="E-mail: "
|
||||
clipPasswordResource="Password: "
|
||||
tooltipPasswordTitle="Password must contain:"
|
||||
tooltipPasswordLength="from 6 to 30 characters"
|
||||
tooltipPasswordDigits="digits"
|
||||
tooltipPasswordCapital="capital letters"
|
||||
tooltipPasswordSpecial="special characters (!@#$%^&*)"
|
||||
generatorSpecial="!@#$%^&*"
|
||||
passwordSettings={settings}
|
||||
isDisabled={false}
|
||||
placeholder="password"
|
||||
onValidateInput={a => console.log(a)}
|
||||
onCopyToClipboard={b => console.log("Data " + b + " copied to clipboard")}
|
||||
/>
|
||||
);
|
||||
const wrapper = mount(<PasswordInput {...baseProps} />);
|
||||
|
||||
expect(wrapper).toExist();
|
||||
});
|
||||
|
||||
it('render password input', () => {
|
||||
const wrapper = mount(<PasswordInput {...baseProps} />);
|
||||
|
||||
expect(wrapper.find('input').prop('type')).toEqual('password');
|
||||
});
|
||||
|
||||
it('have an HTML name', () => {
|
||||
const wrapper = mount(<PasswordInput {...baseProps} />);
|
||||
|
||||
expect(wrapper.find('input').prop('name')).toEqual('demoPasswordInput');
|
||||
});
|
||||
|
||||
it('forward passed value', () => {
|
||||
const wrapper = mount(<PasswordInput {...baseProps} inputValue='demo' />);
|
||||
|
||||
expect(wrapper.props().inputValue).toEqual('demo');
|
||||
});
|
||||
|
||||
it('call onChange when changing value', () => {
|
||||
const onChange = jest.fn(event => {
|
||||
expect(event.target.id).toEqual('demoPasswordInput');
|
||||
expect(event.target.name).toEqual('demoPasswordInput');
|
||||
expect(event.target.value).toEqual('demo');
|
||||
});
|
||||
|
||||
const wrapper = mount(<PasswordInput {...baseProps} id="demoPasswordInput" name="demoPasswordInput" onChange={onChange} />);
|
||||
|
||||
const event = { target: { value: "demo" } };
|
||||
|
||||
wrapper.simulate('change', event);
|
||||
});
|
||||
|
||||
it('call onFocus when input is focused', () => {
|
||||
const onFocus = jest.fn(event => {
|
||||
expect(event.target.id).toEqual('demoPasswordInput');
|
||||
expect(event.target.name).toEqual('demoPasswordInput');
|
||||
});
|
||||
|
||||
const wrapper = mount(<PasswordInput {...baseProps} id="demoPasswordInput" name="demoPasswordInput" onFocus={onFocus} />);
|
||||
|
||||
wrapper.simulate('focus');
|
||||
});
|
||||
|
||||
it('call onBlur when input loses focus', () => {
|
||||
const onBlur = jest.fn(event => {
|
||||
expect(event.target.id).toEqual('demoPasswordInput');
|
||||
expect(event.target.name).toEqual('demoPasswordInput');
|
||||
});
|
||||
|
||||
const wrapper = mount(<PasswordInput {...baseProps} id="demoPasswordInput" name="demoPasswordInput" onBlur={onBlur} />);
|
||||
|
||||
wrapper.simulate('blur');
|
||||
});
|
||||
|
||||
it('disabled when isDisabled is passed', () => {
|
||||
const wrapper = mount(<PasswordInput {...baseProps} isDisabled={true} />);
|
||||
|
||||
expect(wrapper.prop('isDisabled')).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user