Skip to content

Commit 9db2c66

Browse files
author
Manor
committed
feat(webhooks): webhooks
webhooks
1 parent 244a400 commit 9db2c66

File tree

24 files changed

+1060
-29
lines changed

24 files changed

+1060
-29
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import React, { Component } from 'react'
2+
import PropTypes from 'prop-types'
3+
import css from './LabeledCheckbox.scss'
4+
import Checkbox from '../Checkbox/Checkbox'
5+
import classnames from 'classnames'
6+
7+
class LabeledCheckbox extends Component {
8+
render () {
9+
const { children, className, checkboxClassName, indeterminate, checked, disabled, onChange, ...rest } = this.props
10+
return (
11+
<span
12+
{...rest}
13+
className={classnames(className, css.wrapper, { [css.disabled]: disabled })}
14+
onClick={() => !disabled && onChange(!checked)}
15+
>
16+
<span className={classnames(css.input_wrapper, checkboxClassName)}>
17+
<Checkbox
18+
indeterminate={indeterminate}
19+
checked={checked}
20+
disabled={disabled}
21+
/>
22+
</span>
23+
<label className={css.label_wrapper}>
24+
{children}
25+
</label>
26+
</span>
27+
)
28+
}
29+
}
30+
31+
LabeledCheckbox.propTypes = {
32+
children: PropTypes.oneOfType([
33+
PropTypes.node,
34+
PropTypes.arrayOf(PropTypes.node)
35+
]),
36+
className: PropTypes.string,
37+
indeterminate: PropTypes.bool,
38+
checked: PropTypes.bool,
39+
disabled: PropTypes.bool,
40+
onChange: PropTypes.func,
41+
checkboxClassName: PropTypes.string
42+
}
43+
LabeledCheckbox.defaultProps = {
44+
indeterminate: false,
45+
checked: false,
46+
disabled: false
47+
}
48+
49+
export default LabeledCheckbox
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
@import "../styles/colors";
2+
3+
.wrapper {
4+
display: flex;
5+
align-items: center;
6+
width: 100%;
7+
cursor: pointer;
8+
&.disabled {
9+
cursor: default;
10+
}
11+
12+
.label_wrapper {
13+
font-size: 13px;
14+
color: $text-black;
15+
cursor: pointer;
16+
position: relative;
17+
bottom: 1px;
18+
flex: 1;
19+
min-width: 0;
20+
}
21+
}
22+
23+
.input_wrapper {
24+
margin-right: 10px;
25+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/* eslint-env jest */
2+
3+
import React from 'react'
4+
import { mount } from 'enzyme'
5+
import CheckboxGroup from '..'
6+
import LabeledCheckbox from '../../../../LabeledCheckbox/LabeledCheckbox.export'
7+
8+
const mountComponent = (props) => {
9+
return mount(<CheckboxGroup {...props} />)
10+
}
11+
12+
const OPTIONS = [
13+
{
14+
key: 'key-a',
15+
value: 'value-a'
16+
}, {
17+
key: 'key-b',
18+
value: 'value-b'
19+
}, {
20+
key: 'key-c',
21+
value: 'value-c'
22+
}
23+
]
24+
25+
describe('<CheckboxGroup />', () => {
26+
let checkBoxGroupComponent, defaultProps
27+
28+
beforeAll(() => {
29+
defaultProps = {
30+
options: OPTIONS,
31+
checkedOptions: []
32+
}
33+
})
34+
35+
describe('PROPS', () => {
36+
describe('Options', () => {
37+
it('Should render an empty group if options is not given', () => {
38+
// Arrange
39+
const props = Object.assign({}, defaultProps, { options: undefined })
40+
checkBoxGroupComponent = mountComponent(props)
41+
42+
// Assert
43+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
44+
expect(renderedOptions).toHaveLength(0)
45+
})
46+
it('Should render an empty group if options is an empty array', () => {
47+
// Arrange
48+
const props = Object.assign({}, defaultProps, { options: [] })
49+
checkBoxGroupComponent = mountComponent(props)
50+
51+
// Assert
52+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
53+
expect(renderedOptions).toHaveLength(0)
54+
})
55+
it('Should render the given options as a list of LabeledCheckbox components', () => {
56+
// Arrange
57+
checkBoxGroupComponent = mountComponent(defaultProps)
58+
59+
// Assert
60+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
61+
expect(renderedOptions).toHaveLength(OPTIONS.length)
62+
renderedOptions.forEach((option, i) => {
63+
expect(option.text()).toEqual(OPTIONS[i].value)
64+
})
65+
})
66+
})
67+
68+
describe('CheckedOptions', () => {
69+
it('Should render a list of unchecked LabeledCheckbox components - empty check options', () => {
70+
// Arrange
71+
const props = Object.assign({}, defaultProps, { checkedOptions: undefined })
72+
checkBoxGroupComponent = mountComponent(props)
73+
74+
// Assert
75+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
76+
renderedOptions.forEach((option) => {
77+
expect(option.prop('checked')).toBe(false)
78+
})
79+
})
80+
it('Should render a list of unchecked LabeledCheckbox components', () => {
81+
// Arrange
82+
checkBoxGroupComponent = mountComponent(defaultProps)
83+
84+
// Assert
85+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
86+
renderedOptions.forEach((option) => {
87+
expect(option.prop('checked')).toBe(false)
88+
})
89+
})
90+
it('Should render the given checkedOptions as a checked LabeledCheckbox components', () => {
91+
// Arrange
92+
const givenCheckedOption = OPTIONS[0]
93+
const props = Object.assign({}, defaultProps, { checkedOptions: [givenCheckedOption.key] })
94+
checkBoxGroupComponent = mountComponent(props)
95+
96+
// Assert
97+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
98+
renderedOptions.forEach((option) => {
99+
const isGivenCheckedOption = givenCheckedOption.value.includes(option.text())
100+
expect(option.prop('checked')).toBe(isGivenCheckedOption)
101+
})
102+
})
103+
})
104+
105+
describe('ClassName', () => {
106+
it('Should NOT pass any className property to the wrapper component div if className is not given', () => {
107+
// Arrange
108+
checkBoxGroupComponent = mountComponent(defaultProps)
109+
110+
// Assert
111+
const optionsGroupComponent = checkBoxGroupComponent.find('[data-test="checkbox-group"]')
112+
expect(optionsGroupComponent.prop('className')).toBe(undefined)
113+
})
114+
115+
it('Should pass the given className to the wrapper component div', () => {
116+
// Arrange
117+
const givenClassName = 'this-is-className'
118+
const props = Object.assign({}, defaultProps, { className: givenClassName })
119+
checkBoxGroupComponent = mountComponent(props)
120+
121+
// Assert
122+
const optionsGroupComponent = checkBoxGroupComponent.find('[data-test="checkbox-group"]')
123+
expect(optionsGroupComponent.prop('className')).toBe(givenClassName)
124+
})
125+
})
126+
127+
describe('OnChange', () => {
128+
it('Should call onChange when option checked status is changed - Selected', () => {
129+
// Arrange
130+
const checkedOptionIndex = 0
131+
const props = Object.assign({}, defaultProps, { onChange: jest.fn() })
132+
checkBoxGroupComponent = mountComponent(props)
133+
134+
// Act - check the first option
135+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
136+
renderedOptions.at(checkedOptionIndex).simulate('click')
137+
138+
// Assert - onChange is called
139+
expect(props.onChange.mock.calls.length).toBe(1)
140+
expect(props.onChange.mock.calls[0][0]).toEqual([OPTIONS[checkedOptionIndex].key])
141+
})
142+
it('Should call onChange when option checked status is changed - Unselected', () => {
143+
// Arrange
144+
const checkedOptionIndex = 0
145+
const props = Object.assign({}, defaultProps, {
146+
checkedOptions: [OPTIONS[checkedOptionIndex].key],
147+
onChange: jest.fn()
148+
})
149+
checkBoxGroupComponent = mountComponent(props)
150+
151+
// Act - un-check the first option
152+
const renderedOptions = checkBoxGroupComponent.find(LabeledCheckbox)
153+
expect(renderedOptions.at(checkedOptionIndex).prop('checked')).toBe(true)
154+
renderedOptions.at(checkedOptionIndex).simulate('click')
155+
156+
// Assert - onChange is called
157+
expect(props.onChange.mock.calls.length).toBe(1)
158+
expect(props.onChange.mock.calls[0][0]).toEqual([])
159+
})
160+
})
161+
})
162+
})
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React, { Component } from 'react'
2+
import PropTypes from 'prop-types'
3+
import _ from 'lodash'
4+
5+
import OptionItem from '../OptionItem'
6+
import { removeItem, addItem } from '../../Utils/Arrays'
7+
8+
class CheckboxGroup extends Component {
9+
constructor (props) {
10+
super(props)
11+
this.state = {
12+
checkedOptions: this.props.checkedOptions.map((option) => _.get(option, 'key')) || []
13+
}
14+
}
15+
16+
static getDerivedStateFromProps (props) {
17+
return props.checkedOptions ? { checkedOptions: props.checkedOptions } : null
18+
}
19+
20+
onCheckBoxItemCheck = optionKey => {
21+
const { checkedOptions } = this.state
22+
const isChecked = checkedOptions.includes(optionKey)
23+
24+
const currentCheckedOptions = isChecked
25+
? removeItem(checkedOptions, optionKey)
26+
: addItem(checkedOptions, optionKey).sort()
27+
28+
this.setState({ checkedOptions: currentCheckedOptions })
29+
30+
this.props.onChange(currentCheckedOptions)
31+
}
32+
33+
render () {
34+
const { checkedOptions } = this.state
35+
const { options, className, checkedOptions: checkedOptionsFromProps, ...rest } = this.props
36+
37+
const buildCheckBoxItems = () => {
38+
return options.map((option, i) => {
39+
const key = _.get(option, 'key')
40+
return (
41+
<OptionItem
42+
data-test='option'
43+
key={`${i}_${key}`}
44+
checked={checkedOptions.includes(key)}
45+
onChange={() => this.onCheckBoxItemCheck(key)}
46+
>
47+
{option.value}
48+
</OptionItem>
49+
)
50+
})
51+
}
52+
53+
return (
54+
<div data-test='checkbox-group' className={className} {...rest}>
55+
{buildCheckBoxItems()}
56+
</div>
57+
)
58+
}
59+
}
60+
61+
CheckboxGroup.propTypes = {
62+
className: PropTypes.string,
63+
options: PropTypes.arrayOf(PropTypes.any).isRequired,
64+
checkedOptions: PropTypes.arrayOf(PropTypes.string),
65+
onChange: PropTypes.func.isRequired
66+
}
67+
68+
CheckboxGroup.defaultProps = {
69+
options: [],
70+
checkedOptions: [],
71+
onChange: _.noop
72+
}
73+
74+
export default CheckboxGroup

0 commit comments

Comments
 (0)