Skip to content

Instantly share code, notes, and snippets.

@konstantin24121
Created May 1, 2018 08:56
Show Gist options
  • Save konstantin24121/4705f02b94c7b065b564684d96a4d388 to your computer and use it in GitHub Desktop.
Save konstantin24121/4705f02b94c7b065b564684d96a4d388 to your computer and use it in GitHub Desktop.
Old component with pcss and flow

Текстовое поле для ввода текстовой информации, позволяет пользователям вводить данные.

<TextField name="test_field" value="Just a value" placeholder="Simple placebobr"/>

Текстовое поле с плавающей меткой

<TextField name="the_field" placeholder="Put text here" floatingLabel="label"/>

Поле с подсказкой. Подсказка может использоватся как указание до ввода данных для пользователя что вводить в поле ввода или же для показа ошибок валидации уже после ввода.

<TextField
  name="the_field"
  value="Wrong value"
  status="danger"
  hint="You put wrong value idiot"
  floatingLabel="label"
/>

Иногда поле может быть неактивно, для этого ему можно просто передать атрибут disabled, совсем как в HTML не правда ли

<TextField
  name="the_field"
  placeholder="This field disable"
  floatingLabel="label"
  disabled={true}
  onFocus={() => {console.log('Focus mother fucker, can you do it?')}}
/>
import React from 'react';
import TextField from '../index';
const newValue = 'nezad';
const Event = {
target: {
value: newValue,
},
};
describe('TextField', () => {
it('Render a TextField with correct props and default callbacks are fired', () => {
const rc = shallow(
<TextField
name="field"
placeholder="Placehodor"
hint="Hint Ladger"
/>
);
expect(rc).toMatchSnapshot();
rc.find('input').simulate('focus');
rc.update();
expect(rc).toMatchSnapshot();
rc.find('input').simulate('blur');
rc.update();
expect(rc).toMatchSnapshot();
rc.find('input').simulate('change', Event);
rc.update();
expect(rc).toMatchSnapshot();
});
it('Focus at TextField with float label mast have effect', () => {
const rc = shallow(
<TextField name="field" floatingLabel="label" />
);
expect(rc).toMatchSnapshot();
// Simulate focus event by input
rc.find('input').simulate('focus');
rc.update();
expect(rc).toMatchSnapshot();
});
it('Blur callback at TextField when input generate blur event', () => {
const onBlur = jest.fn();
const rc = shallow(
<TextField name="field" onBlur={onBlur} />
);
// Simulate blur event by input
rc.find('input').simulate('blur');
expect(onBlur.mock.calls.length).toBe(1);
});
it('TextField change value if receive new props', () => {
const rc = shallow(
<TextField name="field" value="zad" />
);
rc.setProps({ value: 'nezad' });
expect(rc.find('input').props().value).toBe('nezad');
});
it('TextField mast give value when he changed', () => {
const onChange = jest.fn(({ value }) => value);
const rc = shallow(
<TextField name="field" value="zad" onChange={onChange} />
);
rc.find('input').simulate('change', Event);
expect(onChange).toBeCalledWith({ value: newValue }, Event);
});
});
// @flow
import React, { PureComponent } from 'react';
// Helpers
import classNameBind from 'classnames/bind';
import { upperFirst } from 'utils';
import s from './TextField.pcss';
const cn = classNameBind.bind(s);
type Props = {
/**
* Имя поля
*/
name: string,
/**
* Значение поля
*/
value: string,
/**
* Placeholder для поля
*/
placeholder: string,
/**
* Плавающий label
*/
floatingLabel: string,
/**
* Подсказка для поля ввода
* самое распространненное использование в качестве
* показа ошибок валидации
*/
hint?: string,
/**
* Статус поля
*/
status: 'normal' | 'warning' | 'danger',
/**
* Деактивировать поле
*/
disabled: boolean,
/**
* Срабатывает при изменении value в поле ввода
*/
onChange: (
args: {
value: string,
},
e: SyntheticInputEvent,
) => void,
/**
* Срабатывает при получении фокуса полем
*/
onFocus: (
e: SyntheticInputEvent,
) => void,
/**
* Срабатывает при потере фокуса полем
*/
onBlur: (
e: SyntheticInputEvent,
) => void,
};
type State = {
value: string,
isFocused: boolean,
isDirty: boolean,
};
/**
* Поле для ввода текста, компонент может использоватся
* как обычный input, так и примитивный textarea
* @type {ReactPureComponent}
* @name TextField
* @namespace components
* @version 0.1.1
*/
class TextField extends PureComponent {
state: State;
props: Props;
static defaultProps = {
value: '',
placeholder: '',
floatingLabel: '',
status: 'normal',
disabled: false,
/* eslint-disable no-unused-vars */
onChange: ({ value }, event) => {},
onFocus: (event) => {},
onBlur: (event) => {},
/* eslint-enable no-unused-vars */
};
constructor(props: Props): void {
super(props);
this.state = {
value: props.value,
isFocused: false,
/**
* Поле было затронуто пользователем
* как церковный мальчик католическим священником
*/
isDirty: false,
};
}
componentWillReceiveProps(nextProps: Props): void {
this.setState({
value: nextProps.value,
});
}
/**
* Handles
*/
handleFocus = (e: SyntheticInputEvent): void => {
this.setState({
isFocused: true,
isDirty: true,
});
this.props.onFocus(e);
};
handleBlur = (e: SyntheticInputEvent): void => {
this.setState({
isFocused: false,
});
this.props.onBlur(e);
};
handleChange = (e: SyntheticInputEvent): void => {
const { value } = e.target;
this.setState({ value });
this.props.onChange({ value }, e);
}
/**
* Renders
*/
render(): ?React$Element<any> {
const { name, placeholder, floatingLabel, status, hint, disabled } = this.props;
const { value, isFocused, isDirty } = this.state;
const isEmpty = !value;
const hasFloatingLabel = !!floatingLabel;
const isLabelFloating = !isEmpty || isFocused;
const rootCn = cn({
root: true,
[`root_is${upperFirst(status)}`]: true,
root_isFocused: isFocused,
root_isDirty: isDirty,
root_isDisabled: disabled,
root_hasFloatingLabel: hasFloatingLabel,
root_hasHint: !!hint,
});
const labelCn = cn({
label: true,
label_isFloat: isLabelFloating,
});
const placeholderCn = cn({
placeholder: true,
placeholder_isVisible: (isLabelFloating && isEmpty)
|| (!hasFloatingLabel && isEmpty),
});
const inputCn = cn({ input: true });
const underlineCn = cn({ underline: true });
const underlineStaticCn = cn({ underlineStatic: true });
const underlineDynamicCn = cn({ underlineDynamic: true });
const hintCn = cn({ hint: true });
return (
<div className={rootCn}>
{placeholder && <div className={placeholderCn}>{placeholder}</div>}
{floatingLabel && <div className={labelCn}>{floatingLabel}</div>}
<input
type="text"
name={name}
value={value}
className={inputCn}
disabled={disabled}
onChange={this.handleChange}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
/>
<div className={underlineCn}>
<hr className={underlineStaticCn} />
<hr className={underlineDynamicCn} />
</div>
{hint && <div className={hintCn}>{hint}</div>}
</div>
);
}
}
export default TextField;
.root {
display: inline-block;
width: 100%;
position: relative;
height: 48px;
line-height: 24px;
font-size: 1rem;
&_isNormal {
/* Normal state */
color: rgb(0, 188, 212);
}
&_isWarning {
color: rgb(255, 152, 0);
}
&_isDanger {
color: rgb(244, 67, 54);
}
&_isFocused {
/* Focus state */
}
&_isDirty {
/* When user touch field */
}
&_isDisabled {
opacity: 0.8;
user-select: none;
pointer-events: none;
cursor: not-allowed;
}
&_hasFloatingLabel {
margin-top: 14px;
}
&_hasHint {
margin-bottom: 14px;
}
}
.placeholder {
position: absolute;
bottom: 12px;
opacity: 0;
color: rgba(0, 0, 0, 0.298039);
transition: opacity 350ms cubic-bezier(0.23, 1, 0.32, 1);
user-select: none;
pointer-events: none;
z-index: 1;
&__is_visible {
opacity: 1;
}
}
.label {
position: absolute;
bottom: 12px;
color: rgba(0, 0, 0, 0.298039);
transform-origin: left top 0;
transition-property: transform, color;
transition-duration: 450ms;
transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
user-select: none;
pointer-events: none;
z-index: 1;
&_isFloat {
color: inherit;
transform: scale(0.75) translate(0, -28px);
}
}
.input {
/* Clean default styles */
border: none;
outline: none;
height: 100%;
width: 100%;
background-color: transparent;
position: relative;
z-index: 2;
cursor: inherit;
}
.underlineStatic,
.underlineDynamic {
box-sizing: content-box;
border-width: 0;
border-color: rgb(224, 244, 244);
border-style: solid;
border-bottom-width: 1px;
margin: 0;
position: absolute;
width: 100%;
height: 0;
bottom: 8px;
.root_isDanger & {
border-color: rgb(244, 67, 54);
/* Progressive */
border-color: currentColor;
}
}
.underlineDynamic {
border-bottom-width: 2px;
transform: scaleX(0);
transition: transform 350ms cubic-bezier(0.23, 1, 0.32, 1);
.root_isNormal & {
border-color: rgb(0, 188, 212);
/* Progressive */
border-color: currentColor;
}
.root_isWarning & {
border-color: rgb(255, 152, 0);
/* Progressive */
border-color: currentColor;
}
.root_isDanger & {
border-color: rgb(244, 67, 54);
/* Progressive */
border-color: currentColor;
}
.root_isFocused &,
.root:focus & {
transform: scaleX(1);
}
}
.hint {
position: relative;
font-size: 0.75em;
color: inherit;
margin-top: -9px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment