Skip to content

Commit

Permalink
Resolve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
MaximKudriavtsev committed Feb 5, 2020
2 parents d49bba6 + b4e937a commit c5d3509
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 78 deletions.
2 changes: 1 addition & 1 deletion docker-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export DEVEXTREME_DOCKER_CI=true
export NUGET_PACKAGES=$PWD/dotnet_packages

function run_lint {
npm i eslint eslint-plugin-spellcheck eslint-plugin-qunit eslint-plugin-jest stylelint stylelint-config-standard npm-run-all babel-eslint
npm i eslint eslint-plugin-spellcheck eslint-plugin-qunit eslint-plugin-jest eslint-plugin-react stylelint stylelint-config-standard npm-run-all babel-eslint
npm run lint
}

Expand Down
74 changes: 48 additions & 26 deletions js/renovation/button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { getImageSourceType } from '../core/utils/icon';
import { Component, Prop, React } from '../component_declaration/common';
import { click } from '../events/short';
import { Component, Effect, Prop, React, Ref } from '../component_declaration/common';
import JSXConstructor from '../component_declaration/jsx';
import Widget from './widget';

Expand Down Expand Up @@ -34,14 +35,6 @@ const getCssClasses = (model: any) => {

export const viewModelFunction = (model: Button) => {
let icon: any = void 0;
const supportedKeys = () => {
const click = (e) => {
e.preventDefault();
model.onClick && model.onClick(e);
};

return { space: click, enter: click };
};

if (model.icon || model.type === 'back') {
icon = getImageContainerJSX(model.icon || 'back');
Expand All @@ -53,12 +46,24 @@ export const viewModelFunction = (model: Button) => {
aria: { label: model.text && model.text.trim() },
cssClasses: getCssClasses(model),
icon,
supportedKeys,
};
};

export const viewFunction = (viewModel: Button) => (
<WidgetJSX
export const viewFunction = (viewModel: Button) => {
const onClick = e => {
viewModel.useSubmitBehavior && viewModel.submitInputRef.current.click();

return viewModel.onClick?.(e);
};

const onKeyPress = (e, { keyName, which }) => {
if (keyName === 'space' || which === 'space' || keyName === 'enter' || which === 'enter') {
e.preventDefault();
onClick(e);
}
};

return <WidgetJSX
accessKey={viewModel.accessKey}
activeStateEnabled={viewModel.activeStateEnabled}
aria={viewModel.aria}
Expand All @@ -69,25 +74,26 @@ export const viewFunction = (viewModel: Button) => (
height={viewModel.height}
hint={viewModel.hint}
hoverStateEnabled={viewModel.hoverStateEnabled}
onClick={viewModel.onClick}
onClick={onClick}
onKeyPress={onKeyPress}
rtlEnabled={viewModel.rtlEnabled}
supportedKeys={viewModel.supportedKeys}
tabIndex={viewModel.tabIndex}
visible={viewModel.visible}
width={viewModel.width}
>
{viewModel.contentRender && (
<div className="dx-button-content">
<viewModel.contentRender icon={viewModel.icon} text={viewModel.text} />
</div>
) || (
<div className="dx-button-content">
{viewModel.icon}
{viewModel.text && <span className="dx-button-text">{viewModel.text}</span>}
</div>
)}
</WidgetJSX>
);
<div className="dx-button-content">
{viewModel.contentRender &&
<viewModel.contentRender icon={viewModel.icon} text={viewModel.text} />}
{!viewModel.contentRender && viewModel.icon}
{!viewModel.contentRender && viewModel.text &&
<span className="dx-button-text">{viewModel.text}</span>
}
{viewModel.useSubmitBehavior &&
<input ref={viewModel.submitInputRef} type="submit" tabIndex={-1} className="dx-button-submit-input"/>
}
</div>
</WidgetJSX>;
};

@Component({
name: 'Button',
Expand All @@ -103,8 +109,24 @@ export default class Button extends Widget {
@Prop() focusStateEnabled?: boolean = true;
@Prop() hoverStateEnabled?: boolean = true;
@Prop() icon?: string;
@Prop() onSubmit?: (e: any) => any = (() => undefined);
@Prop() pressed?: boolean;
@Prop() stylingMode?: string;
@Prop() text?: string = '';
@Prop() type?: string;
@Prop() useSubmitBehavior?: boolean = false;

@Ref() submitInputRef!: HTMLInputElement;

@Effect()
submitEffect() {
const namespace = 'UIFeedback';

click.on(this.submitInputRef, e => {
this.onSubmit?.(e);
e.stopPropagation();
}, { namespace });

return () => click.off(this.widgetRef, { namespace });
}
}
29 changes: 6 additions & 23 deletions js/renovation/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ export default class Widget {
@Prop() name?: string = '';
@Prop() onDimensionChanged: () => any = (() => undefined);
@Prop() onKeyboardHandled?: (args: any) => any | undefined;
@Prop() onKeyPress?: (e: any, options: any) => any = (() => undefined);
@Prop() rtlEnabled?: boolean = config().rtlEnabled;
@Prop() supportedKeys?: (args: any) => any | undefined;
@Prop() tabIndex?: number = 0;
@Prop() visible?: boolean = true;
@Prop() width?: string | number | null = null;
Expand Down Expand Up @@ -337,30 +337,13 @@ export default class Widget {

@Effect()
keyboardEffect() {
const hasKeyboardEventHandler = !!this.onKeyboardHandled;
const shouldAttach = this.focusStateEnabled || hasKeyboardEventHandler;
let id: string | null = null;

if (shouldAttach) {
const keyboardHandler = (options: any) => {
const { originalEvent, keyName, which } = options;
const keys = this.supportedKeys && this.supportedKeys(originalEvent) || {};
const handler = keys[keyName] || keys[which];

if (handler) {
if (!handler(originalEvent, options)) {
return false;
}
}

return true;
};
if (this.focusStateEnabled || this.onKeyPress) {
const id = keyboard.on(this.widgetRef, this.widgetRef,
options => this.onKeyPress?.(options.originalEvent, options));

id = keyboard.on(this.widgetRef, this.widgetRef,
opts => keyboardHandler(opts),
);
return () => keyboard.off(id);
}

return () => keyboard.off(id);
return null;
}
}
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"eslint": "^6.8.0",
"eslint-plugin-jest": "^23.6.0",
"eslint-plugin-qunit": "^4.0.0",
"eslint-plugin-react": "^7.18.0",
"eslint-plugin-spellcheck": "0.0.11",
"exceljs": "3.3.1",
"globalize": "^1.3.0",
Expand Down Expand Up @@ -168,5 +169,10 @@
"ie > 10",
"> 1%"
],
"jest": {
"modulePathIgnorePatterns": [
"node_modules"
]
},
"pre-commit": "lint-staged"
}
9 changes: 7 additions & 2 deletions testing/jest/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
"jest": true
},
"plugins": [
"jest"
"jest",
"react"
],
"extends": ["plugin:jest/recommended"]
"extends": ["plugin:jest/recommended"],
"rules": {
"no-unused-vars": ["error", { "varsIgnorePattern": "h" }],
"react/jsx-uses-vars": 2
}
}
85 changes: 79 additions & 6 deletions testing/jest/button.tests.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,93 @@
import Button from '../../js/renovation/button.p.js';
import Widget from '../../js/renovation/widget.p.js';
import { createElement } from 'preact';
import { emit, emitKeyboard, EVENT, KEY } from './utils/events-mock';
import { h } from 'preact';
import { clear as clearEventHandlers, defaultEvent, emit, emitKeyboard, EVENT, KEY } from './utils/events-mock';
import { mount } from 'enzyme';

describe('Button', () => {
const render = (props = {}) => mount(createElement(Button, props)).childAt(0);
const render = (props = {}) => mount(<Button {...props} />).childAt(0);

beforeEach(clearEventHandlers);

describe('Props', () => {
describe('useSubmitBehavior', () => {
it('should be "false" by default', () => {
const button = render();

expect(button.exists('.dx-button-submit-input')).toBe(false);
});

it('should render submit input', () => {
const button = render({ useSubmitBehavior: true });
const submitInput = button.find('input.dx-button-submit-input');

expect(submitInput.props()).toMatchObject({
tabIndex: -1,
type: 'submit',
});
});

it('should submit form by button click', () => {
const button = render({ useSubmitBehavior: true });
const submitInput = button.find('input.dx-button-submit-input');
const submitInputClick = jest.fn();

submitInput.getDOMNode().click = submitInputClick;
expect(submitInputClick).toHaveBeenCalledTimes(0);
emit(EVENT.dxClick, defaultEvent, button.getDOMNode());
expect(submitInputClick).toHaveBeenCalledTimes(1);
});

it('should submit form by submit input click', () => {
const onSubmit = jest.fn();
const button = render({ useSubmitBehavior: true, onSubmit });
const submitInput = button.find('input.dx-button-submit-input');

expect(onSubmit).toHaveBeenCalledTimes(0);
emit(EVENT.click, defaultEvent, submitInput.getDOMNode());
expect(onSubmit).toHaveBeenCalledTimes(1);
});

it('should submit form by enter press', () => {
const button = render({ useSubmitBehavior: true });
const submitInput = button.find('input.dx-button-submit-input');
const submitInputClick = jest.fn();

submitInput.getDOMNode().click = submitInputClick;
expect(submitInputClick).toHaveBeenCalledTimes(0);
emitKeyboard(KEY.enter);
expect(submitInputClick).toHaveBeenCalledTimes(1);
});

it('should submit form by sapce press', () => {
const button = render({ useSubmitBehavior: true });
const submitInput = button.find('input.dx-button-submit-input');
const submitInputClick = jest.fn();

submitInput.getDOMNode().click = submitInputClick;
expect(submitInputClick).toHaveBeenCalledTimes(0);
emitKeyboard(KEY.space);
expect(submitInputClick).toHaveBeenCalledTimes(1);
});

it('should stop event propagation', () => {
const button = render({ useSubmitBehavior: true });
const submitInput = button.find('input.dx-button-submit-input');
const e = { ...defaultEvent, stopPropagation: jest.fn() };

expect(e.stopPropagation).toHaveBeenCalledTimes(0);
emit(EVENT.click, e, submitInput.getDOMNode());
expect(e.stopPropagation).toHaveBeenCalledTimes(1);
});
});

describe('onClick', () => {
it('should be called by mouse click', () => {
const clickHandler = jest.fn();
const button = render({ onClick: clickHandler });

render({ onClick: clickHandler });
expect(clickHandler).toHaveBeenCalledTimes(0);
emit(EVENT.click);
emit(EVENT.dxClick, defaultEvent, button.getDOMNode());
expect(clickHandler).toHaveBeenCalledTimes(1);
});

Expand Down Expand Up @@ -115,7 +188,7 @@ describe('Button', () => {
it('should render template', () => {
const button = render({
text: 'My button',
contentRender: ({ text }) => createElement('div', { className: 'custom-content', children: `${text}123` }),
contentRender: ({ text }) => <div className={'custom-content'}>{text + 123}</div>,
});
const buttonContentChildren = button.find('.dx-button-content').children();

Expand Down
17 changes: 12 additions & 5 deletions testing/jest/utils/events-mock.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import eventsEngine from '../../../js/events/core/events_engine';
import { keyboard } from '../../../js/events/short';

const eventHandlers = {};
const keyboardHandlers = {};
let eventHandlers = {};
let keyboardHandlers = {};

export const KEY = {
enter: 'enter',
space: 'space'
};

export const clear = () => {
eventHandlers = {};
keyboardHandlers = {};
};

export const EVENT = {
active: 'dxactive',
blur: 'focusout',
click: 'dxclick',
click: 'click',
dxClick: 'dxclick',
focus: 'focusin',
hoverEnd: 'dxhoverend',
hoverStart: 'dxhoverstart',
Expand All @@ -21,9 +27,10 @@ export const EVENT = {
shown: 'dxshown'
};

const defaultEvent = {
export const defaultEvent = {
stopImmediatePropagation: () => void 0,
preventDefault: () => void 0
preventDefault: () => void 0,
stopPropagation: () => void 0,
};

export const fakeClickEvent = Object.assign(defaultEvent, {
Expand Down
Loading

0 comments on commit c5d3509

Please sign in to comment.