Skip to content
This repository has been archived by the owner on Aug 30, 2019. It is now read-only.

Dropdown and Checkbox component #49

Merged
merged 34 commits into from
Apr 4, 2017
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
bfe3b4e
init dropdown component
AlleeX Mar 16, 2017
66f823e
test commit
Mar 16, 2017
68a244c
test commit2
Mar 16, 2017
d0e4ec8
add react select
AlleeX Mar 19, 2017
a2e2a6f
Merge branch 'dropdown' of github.com:AlleeX/travix-ui-kit into dropdown
AlleeX Mar 19, 2017
5a3e147
add styles and examples for dropdown
AlleeX Mar 20, 2017
695375b
added initial unit tests for dropdown
Mar 20, 2017
f1d72eb
Merge branch 'master' into dropdown
AlleeX Mar 21, 2017
913bd6c
improve styles for dd
AlleeX Mar 22, 2017
e75300a
add checkbox
AlleeX Mar 22, 2017
ddd0f7f
add filter mode for dropdown component
AlleeX Mar 23, 2017
604bf2b
Merge branch 'dropdown' of github.com:AlleeX/travix-ui-kit into dropdown
AlleeX Mar 23, 2017
ed0a273
delete unnecessary code
AlleeX Mar 23, 2017
441b6c5
resolve conflicts
AlleeX Mar 23, 2017
7bed718
edit md
AlleeX Mar 23, 2017
fcee961
add unit tests for checkbox
AlleeX Mar 23, 2017
7110208
updated snapshots and added unit tests
Mar 23, 2017
5136372
resolved merge conflict
Mar 23, 2017
6f343c8
add unit tests
AlleeX Mar 24, 2017
84efbc2
rollback
AlleeX Mar 24, 2017
685b524
merge conflicts
AlleeX Mar 24, 2017
ef5a7f2
resolve merge conflict
AlleeX Apr 3, 2017
b8485ed
resolve merge conflict
AlleeX Apr 3, 2017
57a0882
100% dropdown unit tests coverage
AlleeX Apr 3, 2017
cbeeefd
remove unneeded properties from checkbox component
AlleeX Apr 3, 2017
9292d7f
fix PR issues
AlleeX Apr 3, 2017
f300722
fix unit tests
AlleeX Apr 3, 2017
a3eeb71
delete vendors files
AlleeX Apr 3, 2017
251e176
Merge branch 'master' into dropdown
AlleeX Apr 3, 2017
1f7d351
fix building
AlleeX Apr 3, 2017
c006d59
fix unit test
AlleeX Apr 3, 2017
b38a9f6
improve import
AlleeX Apr 3, 2017
55a458b
refactoring
AlleeX Apr 4, 2017
d68bebf
make code more beauty
AlleeX Apr 4, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
76 changes: 76 additions & 0 deletions components/checkbox/checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { PropTypes } from 'react';
import { getClassNamesWithMods } from '../_helpers';

/**
* Checkbox component.
*/
function Checkbox({
checked,
children,
disabled,
mods = [],
name,
onChange,
}) {
disabled && mods.push('is-disabled');
const className = getClassNamesWithMods('ui-checkbox', mods);

return (
<label className={className} htmlFor={name}>
<input
aria-checked={checked}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lovely ❤️

checked={checked}
disabled={disabled}
id={name}
onChange={onChange}
role="radio"
type="checkbox"
/>
<span className="ui-checkbox__text" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have a closed <span>? Shouldn't it wrap the {children}?

{children}
</label>
);
}

Checkbox.defaultProps = {
checked: false,
disabled: false,
name: '',
};

Checkbox.propTypes = {
/**
* Represents the state of the checkbox.
*/
checked: PropTypes.bool,

/**
* Checkbox label.
*/
children: PropTypes.oneOfType([
PropTypes.string,
PropTypes.element,
]),

/**
* Current activity state of the checkbox.
*/
disabled: PropTypes.bool,

/**
* You can provide set of custom modifications.
*/
mods: PropTypes.arrayOf(PropTypes.string),

/**
* Represents the element's name.
*/
name: PropTypes.string.isRequired,

/**
* Represents the callback for onChange event.
*/
onChange: PropTypes.func.isRequired,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EduardTrutsyk good question. I am assuming it's because this component is a controlled component and therefore it requires an onChange definition (more at: https://facebook.github.io/react/docs/forms.html )

However, I do agree that either we decide to not have a controlled component (therefore the style and value will come from the normal checked attribute on the checkbox, or we go for this approach and then we'll need state, since you need to change the checked attribute (since you can't mutate props nor change them from the inside).

};

export default Checkbox;
16 changes: 16 additions & 0 deletions components/checkbox/checkbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Basic checkbox:

<div>
<div>
<Checkbox name="checkboxTest1" onChange={() => {alert('without text')}}/><br/><br/>
<Checkbox name="checkboxTest2" checked onChange={() => {alert('checked')}}>checked</Checkbox><br/>
<Checkbox name="checkboxTest3" disabled onChange={() => {alert('disabled')}}>disabled</Checkbox><br/>
<Checkbox
name="checkboxTest4"
checked={state.value}
onChange={() => {setState({value: !state.value})}}
inputAttr={{role: "radio"}}>
can toggle
</Checkbox>
</div>
</div>
82 changes: 82 additions & 0 deletions components/checkbox/checkbox.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.ui-checkbox {
cursor: pointer;
display: flex;
font-family: $tx-generic-font-secondary-font-family, $tx-generic-font-secondary-generic-family;
font-weight: $tx-generic-font-primary-weight-regular;
padding: 0;
user-select: none;

&.ui-checkbox_is-disabled {
cursor: default;
color: rgba($tx-checkbox-text-disabled-color, .3);
}

.ui-checkbox__text {
font-size: 13px;
}

input[type="checkbox"] {
display: none;

& ~ .ui-checkbox__text {
color: $tx-checkbox-text-color;
padding-left: 25px;
position: relative;
-webkit-tap-highlight-color: transparent;

&:before {
background-color: $tx-checkbox-text-background-color;
border: 2px solid $tx-checkbox-text-border-color;
content: '';
height: 12px;
left: 0;
position: absolute;
top: 0;
width: 12px;
}
}

& ~ .ui-checkbox__text:before {
border-radius: 3px;
}

& ~ .ui-checkbox__text:hover {
color: $tx-checkbox-text-hover-color;
}

&:checked {
& ~ .ui-checkbox__text {
&:before {
background: $tx-checkbox-text-checked-background-color;
border-color: $tx-checkbox-text-checked-border-color;
border-radius: 3px;
}

&:after {
background: transparent;
border: 3px solid $tx-checkbox-text-icon-checked-border-color;
border-radius: 3px;
border-right: none;
border-top: none;
content: '';
height: 4px;
left: 3px;
position: absolute;
top: 3px;
transform: rotate(-45deg);
width: 7px;
}
}
}

&:disabled {
& ~ .ui-checkbox__text {
color: rgba($tx-checkbox-text-disabled-color, .3);

&:before {
border-color: rgba($tx-checkbox-text-disabled-border-color, .3);
}
}
}
}
}
166 changes: 166 additions & 0 deletions components/dropDown/dropDown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React, { Component, PropTypes } from 'react';
import Select from 'react-select';

import { getClassNamesWithMods } from '../_helpers';
import DropdownFilterOptionComponent from './dropdownFilterOptionComponent';

/**
* DropDown component
*/
class DropDown extends Component {
constructor(props) {
super(props);

this.onChange = this.onChange.bind(this);
this.menuRenderer = this.menuRenderer.bind(this);
}

optionRef = (onOptionRef, isSelected) => ref => onOptionRef(ref, isSelected);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change it to a simple method instead of an arrow function? You're returning another function it gets complicated to read:

static optionRef(onOptionRef, isSelected) {
  return ref => onOptionRef(ref, isSelected);
},

And then change its usage to: Dropdown.optionRef(), since it doesn't require to be bound to this.


/**
* Overriding the internal method of react-select for fix autoscrolling
*/
menuRenderer({
focusedOption,
instancePrefix,
onFocus,
onSelect,
optionClassName,
optionComponent,
optionRenderer,
options,
valueArray,
valueKey,
onOptionRef,
}) {
let Option = optionComponent;

return options.map((option, i) => {
let isSelected = valueArray && valueArray.indexOf(option) > -1;
let isFocused = option === focusedOption;
const classes = ['Select-option'];

optionClassName && classes.push(optionClassName);
isSelected && classes.push('is-selected');
isFocused && classes.push('is-focused');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now, you can use object instead of array, to apply classes based on props:

const mods = {
  'is-focused': isFocused,
  'is-selecte': isSelected
};

<Option classNames={getClassNamesWithMods('Select-option', mods)} />

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need class names without concatenation

option.disabled && classes.push('is-disabled');
const optionClass = classes.join(' ');

return (
<Option
className={optionClass}
instancePrefix={instancePrefix}
isDisabled={option.disabled}
isFocused={this.props.filterMode ? false : isFocused}
isSelected={isSelected}
key={`option-${i}-${option[valueKey]}`}
onFocus={onFocus}
onSelect={onSelect}
option={option}
optionIndex={i}
ref={this.optionRef(onOptionRef, isSelected)}
>
{optionRenderer(option, i)}
</Option>
);
});
}

/**
* @param {Array} changedOptions
*/
onChange(changedOptions) {
if (!this.props.filterMode) {
this.props.onChange(changedOptions);
return;
}

const option = changedOptions[0];
const optionIndex = this.props.options.findIndex(o => o.value === option.value);
this.props.onChange(option, optionIndex, this.props.filterKey);
}

render() {
const isFiltermode = this.props.filterMode;
const mods = [];
isFiltermode && mods.push('filter');
isFiltermode && this.props.options.some(option => option.checked) && mods.push('state-active');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here


const className = getClassNamesWithMods('ui-dropdown', mods);

return (
<Select
className={className}
clearable={this.props.clearable}
menuRenderer={this.menuRenderer}
multi={isFiltermode ? true : this.props.multi}
name={this.props.name}
onChange={this.onChange}
optionComponent={isFiltermode ? DropdownFilterOptionComponent : undefined}
options={this.props.options}
placeholder={this.props.placeholder}
scrollMenuIntoView={this.props.scrollMenuIntoView}
searchable={this.props.searchable}
value={this.props.value}
/>
);
}
}

DropDown.defaultProps = {
clearable: false,
filterMode: false,
multi: false,
placeholder: '',
scrollMenuIntoView: false,
searchable: false,
};

DropDown.propTypes = {
/**
* Should it be possible to reset value
*/
clearable: PropTypes.bool,
/**
* Filter key
*/
filterKey: PropTypes.string,
/**
* Filter mode
*/
filterMode: PropTypes.bool,
/**
* Multi-value input
*/
multi: PropTypes.bool,
/**
* Generates a hidden &lt;input /&gt; tag with this field name for html forms
*/
name: PropTypes.string,
/**
* onChange handler: function (newValue) {}
*/
onChange: PropTypes.func.isRequired,
/**
* Array of options
*/
options: PropTypes.array.isRequired,
/**
* Field placeholder, displayed when there's no value
*/
placeholder: PropTypes.string,
/**
* Boolean to enable the viewport to shift so that the full menu fully visible when engaged
*/
scrollMenuIntoView: PropTypes.bool,
/**
* Whether to enable searching feature or not
*/
searchable: PropTypes.bool,
/**
* Initial field value
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array, PropTypes.object]),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PropTypes.any?

};

export default DropDown;