@punch-in/buffet-modern-custom 3.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintignore +7 -0
- package/.eslintrc.js +3 -0
- package/.npmingore +16 -0
- package/README.md +50 -0
- package/babel.config.js +18 -0
- package/build/bundle.development.js +272 -0
- package/build/bundle.production.js +1 -0
- package/build/esm/components/DateTime/Wrapper.js +5 -0
- package/build/esm/components/DateTime/index.js +149 -0
- package/build/esm/components/Header/index.js +86 -0
- package/build/esm/components/Inputs/Wrapper.js +17 -0
- package/build/esm/components/Inputs/index.js +197 -0
- package/build/esm/components/List/index.js +56 -0
- package/build/esm/index.js +4 -0
- package/build/index.js +8 -0
- package/createBuildIndex.js +3 -0
- package/jest.config.js +14 -0
- package/package.json +114 -0
- package/src/components/DateTime/Wrapper.js +13 -0
- package/src/components/DateTime/index.js +150 -0
- package/src/components/DateTime/tests/index.test.js +99 -0
- package/src/components/Header/index.js +84 -0
- package/src/components/Header/tests/index.test.js +46 -0
- package/src/components/Inputs/Wrapper.js +47 -0
- package/src/components/Inputs/index.js +221 -0
- package/src/components/List/index.js +62 -0
- package/src/components/List/tests/index.test.js +11 -0
- package/src/index.js +4 -0
- package/stylelint.config.js +3 -0
- package/test-bundler.js +9 -0
- package/webpack.config.js +64 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* DateTime
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useState, useEffect } from 'react';
|
|
8
|
+
import moment from 'moment';
|
|
9
|
+
import PropTypes from 'prop-types';
|
|
10
|
+
import momentPropTypes from 'react-moment-proptypes';
|
|
11
|
+
import { isEmpty, cloneDeep } from 'lodash';
|
|
12
|
+
import { DatePicker, TimePicker } from '@punch-in/buffet-modern-core';
|
|
13
|
+
import Wrapper from './Wrapper';
|
|
14
|
+
|
|
15
|
+
const UNITS = ['hour', 'minute', 'second'];
|
|
16
|
+
export const getTimeString = time => {
|
|
17
|
+
if (!time) {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const currTime = time || moment();
|
|
22
|
+
|
|
23
|
+
const timeObj = getTimeObject(currTime);
|
|
24
|
+
const timeString = Object.keys(timeObj)
|
|
25
|
+
.map(key => (timeObj[key] < 10 ? `0${timeObj[key]}` : timeObj[key]))
|
|
26
|
+
.join(':');
|
|
27
|
+
|
|
28
|
+
return timeString;
|
|
29
|
+
};
|
|
30
|
+
export const getTimeObject = time => {
|
|
31
|
+
const timeObj = {};
|
|
32
|
+
|
|
33
|
+
UNITS.forEach(unit => {
|
|
34
|
+
timeObj[unit] = time.get(unit);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return timeObj;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function DateTime({
|
|
41
|
+
disabled,
|
|
42
|
+
name,
|
|
43
|
+
onChange,
|
|
44
|
+
value,
|
|
45
|
+
tabIndex,
|
|
46
|
+
step,
|
|
47
|
+
...rest
|
|
48
|
+
}) {
|
|
49
|
+
const [timestamp, setTimestamp] = useState(null);
|
|
50
|
+
|
|
51
|
+
const setData = time => {
|
|
52
|
+
const [hour, minute, second] = time.split(':');
|
|
53
|
+
const timeObj = {
|
|
54
|
+
hour,
|
|
55
|
+
minute,
|
|
56
|
+
second,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const currentDate = isEmpty(timestamp) ? moment() : cloneDeep(timestamp);
|
|
60
|
+
currentDate.set('hours', timeObj.hour);
|
|
61
|
+
currentDate.set('minute', timeObj.minute);
|
|
62
|
+
currentDate.set('second', timeObj.second);
|
|
63
|
+
|
|
64
|
+
setDate(currentDate);
|
|
65
|
+
};
|
|
66
|
+
const setDate = (date, time) => {
|
|
67
|
+
// Clearing the date
|
|
68
|
+
if (date === null) {
|
|
69
|
+
setTimestamp(null);
|
|
70
|
+
|
|
71
|
+
onChange({ target: { name, type: 'datetime', value: null } });
|
|
72
|
+
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const newDate = time || date;
|
|
77
|
+
date.set(getTimeObject(newDate));
|
|
78
|
+
date.toISOString();
|
|
79
|
+
date.format();
|
|
80
|
+
|
|
81
|
+
setTimestamp(date);
|
|
82
|
+
|
|
83
|
+
onChange({ target: { name, type: 'datetime', value: date } });
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!!value && moment(value).isValid()) {
|
|
88
|
+
const newDate = value._isAMomentObject === true ? value : moment(value);
|
|
89
|
+
|
|
90
|
+
setTimestamp(newDate);
|
|
91
|
+
}
|
|
92
|
+
}, [value]);
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<Wrapper>
|
|
96
|
+
<DatePicker
|
|
97
|
+
{...rest}
|
|
98
|
+
name="datetime"
|
|
99
|
+
disabled={disabled}
|
|
100
|
+
onChange={({ target }) => {
|
|
101
|
+
setDate(target.value, timestamp);
|
|
102
|
+
}}
|
|
103
|
+
tabIndex={tabIndex}
|
|
104
|
+
value={timestamp}
|
|
105
|
+
/>
|
|
106
|
+
<TimePicker
|
|
107
|
+
name="time"
|
|
108
|
+
disabled={disabled}
|
|
109
|
+
onChange={({ target }) => {
|
|
110
|
+
setData(target.value);
|
|
111
|
+
}}
|
|
112
|
+
seconds={false}
|
|
113
|
+
tabIndex={tabIndex}
|
|
114
|
+
value={getTimeString(timestamp) || ''}
|
|
115
|
+
step={step}
|
|
116
|
+
/>
|
|
117
|
+
</Wrapper>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
DateTime.defaultProps = {
|
|
122
|
+
autoFocus: false,
|
|
123
|
+
disabled: false,
|
|
124
|
+
id: null,
|
|
125
|
+
onChange: () => {},
|
|
126
|
+
placeholder: null,
|
|
127
|
+
tabIndex: '0',
|
|
128
|
+
value: null,
|
|
129
|
+
withDefaultValue: false,
|
|
130
|
+
step: 30,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
DateTime.propTypes = {
|
|
134
|
+
autoFocus: PropTypes.bool,
|
|
135
|
+
disabled: PropTypes.bool,
|
|
136
|
+
id: PropTypes.string,
|
|
137
|
+
name: PropTypes.string.isRequired,
|
|
138
|
+
onChange: PropTypes.func,
|
|
139
|
+
placeholder: PropTypes.string,
|
|
140
|
+
step: PropTypes.number,
|
|
141
|
+
tabIndex: PropTypes.string,
|
|
142
|
+
value: PropTypes.oneOfType([
|
|
143
|
+
momentPropTypes.momentObj,
|
|
144
|
+
PropTypes.string,
|
|
145
|
+
PropTypes.instanceOf(Date),
|
|
146
|
+
]),
|
|
147
|
+
withDefaultValue: PropTypes.bool,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
export default DateTime;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import moment from 'moment';
|
|
4
|
+
import { act } from 'react-dom/test-utils';
|
|
5
|
+
import { DatePicker, TimePicker } from '@punch-in/buffet-modern-core';
|
|
6
|
+
|
|
7
|
+
import DateTime from '../index';
|
|
8
|
+
|
|
9
|
+
describe('<DateTime />', () => {
|
|
10
|
+
// eslint-disable-next-line jest/expect-expect
|
|
11
|
+
it('Should not crash', () => {
|
|
12
|
+
mount(<DateTime name="datetime" value={null} />);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('Should set the date with moment if a string value is given', () => {
|
|
16
|
+
const value = '2019-03-25T17:14:01.192';
|
|
17
|
+
const props = { name: 'datetime', value };
|
|
18
|
+
const renderedComponent = mount(<DateTime {...props} />);
|
|
19
|
+
const datepicker = renderedComponent.find(DatePicker);
|
|
20
|
+
|
|
21
|
+
expect(datepicker.prop('value')).toEqual(moment(value));
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('Should not set the date with moment if a moment value is given', () => {
|
|
25
|
+
const value = moment();
|
|
26
|
+
const props = { name: 'datetime', value };
|
|
27
|
+
const renderedComponent = mount(<DateTime {...props} />);
|
|
28
|
+
const datepicker = renderedComponent.find(DatePicker);
|
|
29
|
+
|
|
30
|
+
expect(datepicker.prop('value')).toEqual(value);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('Should change the date and keep the current time', () => {
|
|
34
|
+
const value = moment('2019-02-20');
|
|
35
|
+
value.set('hour', 11);
|
|
36
|
+
value.set('minute', 11);
|
|
37
|
+
value.set('second', 11);
|
|
38
|
+
|
|
39
|
+
const props = {
|
|
40
|
+
name: 'datetime',
|
|
41
|
+
onChange: jest.fn(),
|
|
42
|
+
value,
|
|
43
|
+
};
|
|
44
|
+
const renderedComponent = mount(<DateTime {...props} />);
|
|
45
|
+
const datepicker = renderedComponent.find(DatePicker);
|
|
46
|
+
const updatedValue = moment('2019-03-20T10:10:10.000');
|
|
47
|
+
|
|
48
|
+
act(() => {
|
|
49
|
+
datepicker.props().onChange({ target: { value: updatedValue } }, value);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const expected = updatedValue
|
|
53
|
+
.set('hour', 11)
|
|
54
|
+
.set('minute', 11)
|
|
55
|
+
.set('second', 11);
|
|
56
|
+
|
|
57
|
+
expect(renderedComponent.prop('onChange')).toHaveBeenLastCalledWith({
|
|
58
|
+
target: {
|
|
59
|
+
name: 'datetime',
|
|
60
|
+
type: 'datetime',
|
|
61
|
+
value: expected,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('Should change the time and not the date', () => {
|
|
67
|
+
const value = moment('2019-02-20');
|
|
68
|
+
value.set('hour', 11);
|
|
69
|
+
value.set('minute', 11);
|
|
70
|
+
value.set('second', 11);
|
|
71
|
+
|
|
72
|
+
const props = {
|
|
73
|
+
name: 'datetime',
|
|
74
|
+
onChange: jest.fn(),
|
|
75
|
+
value,
|
|
76
|
+
};
|
|
77
|
+
const renderedComponent = mount(<DateTime {...props} />);
|
|
78
|
+
const timepicker = renderedComponent.find(TimePicker);
|
|
79
|
+
|
|
80
|
+
const mock = { target: { value: '10' } };
|
|
81
|
+
|
|
82
|
+
act(() => {
|
|
83
|
+
timepicker.props().onChange(mock);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const expected = value
|
|
87
|
+
.set('hour', 10)
|
|
88
|
+
.set('minute', 11)
|
|
89
|
+
.set('second', 11);
|
|
90
|
+
|
|
91
|
+
expect(renderedComponent.prop('onChange')).toHaveBeenLastCalledWith({
|
|
92
|
+
target: {
|
|
93
|
+
name: 'datetime',
|
|
94
|
+
type: 'datetime',
|
|
95
|
+
value: expected,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Header
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
|
|
10
|
+
import { HeaderTitle, HeaderActions } from '@punch-in/buffet-modern-core';
|
|
11
|
+
import { Header as Wrapper, LoadingBar } from '@punch-in/buffet-modern';
|
|
12
|
+
|
|
13
|
+
function Header({ actions, content, isLoading, stickable, title }) {
|
|
14
|
+
const [isHeaderSticky, setHeaderSticky] = useState(false);
|
|
15
|
+
const headerRef = useRef(null);
|
|
16
|
+
|
|
17
|
+
const { label, cta } = title;
|
|
18
|
+
|
|
19
|
+
const handleScroll = () => {
|
|
20
|
+
if (headerRef.current) {
|
|
21
|
+
setHeaderSticky(headerRef.current.getBoundingClientRect().top <= 20);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
window.addEventListener('scroll', handleScroll);
|
|
27
|
+
|
|
28
|
+
return () => {
|
|
29
|
+
window.removeEventListener('scroll', () => handleScroll);
|
|
30
|
+
};
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Wrapper ref={headerRef}>
|
|
35
|
+
<div
|
|
36
|
+
className={`sticky-wrapper${
|
|
37
|
+
isHeaderSticky && stickable ? ' sticky' : ''
|
|
38
|
+
}`}
|
|
39
|
+
>
|
|
40
|
+
<div className="row">
|
|
41
|
+
<div className="col-sm-6 header-title">
|
|
42
|
+
<HeaderTitle title={label} cta={cta} />
|
|
43
|
+
{isLoading ? <LoadingBar /> : <p>{content}</p>}
|
|
44
|
+
</div>
|
|
45
|
+
<div className="col-sm-6 justify-content-end">
|
|
46
|
+
<HeaderActions actions={actions} />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</Wrapper>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
Header.defaultProps = {
|
|
55
|
+
actions: [],
|
|
56
|
+
content: null,
|
|
57
|
+
isLoading: false,
|
|
58
|
+
stickable: true,
|
|
59
|
+
title: {
|
|
60
|
+
label: null,
|
|
61
|
+
cta: null,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
Header.propTypes = {
|
|
66
|
+
actions: PropTypes.arrayOf(
|
|
67
|
+
PropTypes.shape({
|
|
68
|
+
onClick: PropTypes.func,
|
|
69
|
+
title: PropTypes.string,
|
|
70
|
+
})
|
|
71
|
+
),
|
|
72
|
+
content: PropTypes.string,
|
|
73
|
+
isLoading: PropTypes.bool,
|
|
74
|
+
stickable: PropTypes.bool,
|
|
75
|
+
title: PropTypes.shape({
|
|
76
|
+
cta: PropTypes.shape({
|
|
77
|
+
icon: PropTypes.string,
|
|
78
|
+
onClick: PropTypes.func,
|
|
79
|
+
}),
|
|
80
|
+
label: PropTypes.string,
|
|
81
|
+
}),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default Header;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { mount } from 'enzyme';
|
|
3
|
+
import { HeaderTitle } from '@punch-in/buffet-modern-core';
|
|
4
|
+
import { LoadingBar } from '@punch-in/buffet-modern';
|
|
5
|
+
|
|
6
|
+
import Header from '../index';
|
|
7
|
+
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
title: { label: 'Restaurant', cta: { icon: 'fa fa-pen', onClick: () => {} } },
|
|
10
|
+
content: 'restaurant description',
|
|
11
|
+
callToAction: [],
|
|
12
|
+
};
|
|
13
|
+
const renderComponent = (props = defaultProps) => mount(<Header {...props} />);
|
|
14
|
+
|
|
15
|
+
describe('<Header />', () => {
|
|
16
|
+
// eslint-disable-next-line jest/expect-expect
|
|
17
|
+
it('should not crash', () => {
|
|
18
|
+
renderComponent();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should render title and subtitle', () => {
|
|
22
|
+
const compo = renderComponent();
|
|
23
|
+
const titles = compo.find('.header-title');
|
|
24
|
+
|
|
25
|
+
const title = titles.find(HeaderTitle);
|
|
26
|
+
expect(title.contains('Restaurant')).toEqual(true);
|
|
27
|
+
|
|
28
|
+
const subtitle = titles.find('p');
|
|
29
|
+
expect(subtitle.text()).toEqual('restaurant description');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should not render a LoadingBar if isLoading is false', () => {
|
|
33
|
+
const wrapper = renderComponent();
|
|
34
|
+
|
|
35
|
+
expect(wrapper.find(LoadingBar).exists()).toEqual(false);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should render a LoadingBar if isLoading is true', () => {
|
|
39
|
+
const wrapper = renderComponent({
|
|
40
|
+
...defaultProps,
|
|
41
|
+
isLoading: true,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
expect(wrapper.find(LoadingBar).exists()).toEqual(true);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Wrapper
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import styled, { css } from 'styled-components';
|
|
8
|
+
import { colors, sizes } from '@punch-in/buffet-modern';
|
|
9
|
+
|
|
10
|
+
const Wrapper = styled.div`
|
|
11
|
+
position: relative;
|
|
12
|
+
padding-bottom: ${sizes.margin * 2.7}px;
|
|
13
|
+
label {
|
|
14
|
+
display: block;
|
|
15
|
+
margin-bottom: 1rem;
|
|
16
|
+
}
|
|
17
|
+
> p {
|
|
18
|
+
width: 100%;
|
|
19
|
+
padding-top: 10px;
|
|
20
|
+
font-size: 13px;
|
|
21
|
+
line-height: normal;
|
|
22
|
+
white-space: nowrap;
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
text-overflow: ellipsis;
|
|
25
|
+
margin-bottom: -8px;
|
|
26
|
+
}
|
|
27
|
+
input[type='checkbox'] {
|
|
28
|
+
margin-bottom: 13px;
|
|
29
|
+
}
|
|
30
|
+
${({ error }) =>
|
|
31
|
+
!!error &&
|
|
32
|
+
css`
|
|
33
|
+
input,
|
|
34
|
+
textarea,
|
|
35
|
+
select {
|
|
36
|
+
border-color: ${colors.darkOrange};
|
|
37
|
+
}
|
|
38
|
+
`}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const IconWrapper = styled.span`
|
|
42
|
+
margin-left: 5px;
|
|
43
|
+
cursor: pointer;
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
export default Wrapper;
|
|
47
|
+
export { IconWrapper };
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Inputs
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useMemo, useRef, useState } from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
import { get, isEmpty, isFunction, isUndefined } from 'lodash';
|
|
10
|
+
import {
|
|
11
|
+
DatePicker,
|
|
12
|
+
Checkbox,
|
|
13
|
+
Enumeration,
|
|
14
|
+
Error,
|
|
15
|
+
InputNumber,
|
|
16
|
+
InputText,
|
|
17
|
+
Label,
|
|
18
|
+
Select,
|
|
19
|
+
Textarea,
|
|
20
|
+
TimePicker,
|
|
21
|
+
Toggle,
|
|
22
|
+
UnknownInput,
|
|
23
|
+
} from '@punch-in/buffet-modern-core';
|
|
24
|
+
import { Description, ErrorMessage, Tooltip } from '@punch-in/buffet-modern';
|
|
25
|
+
|
|
26
|
+
import DateTime from '../DateTime';
|
|
27
|
+
import Wrapper, { IconWrapper } from './Wrapper';
|
|
28
|
+
/* eslint-disable react/forbid-prop-types */
|
|
29
|
+
|
|
30
|
+
const inputs = {
|
|
31
|
+
bool: Toggle,
|
|
32
|
+
checkbox: Checkbox,
|
|
33
|
+
date: DatePicker,
|
|
34
|
+
datetime: DateTime,
|
|
35
|
+
enum: Enumeration,
|
|
36
|
+
number: InputNumber,
|
|
37
|
+
text: InputText,
|
|
38
|
+
textarea: Textarea,
|
|
39
|
+
time: TimePicker,
|
|
40
|
+
select: Select,
|
|
41
|
+
email: InputText,
|
|
42
|
+
password: InputText,
|
|
43
|
+
search: InputText,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function Inputs({
|
|
47
|
+
customInputs,
|
|
48
|
+
description,
|
|
49
|
+
error: inputError,
|
|
50
|
+
id,
|
|
51
|
+
label,
|
|
52
|
+
name,
|
|
53
|
+
onBlur: handleBlur,
|
|
54
|
+
onChange,
|
|
55
|
+
translatedErrors,
|
|
56
|
+
type,
|
|
57
|
+
validations,
|
|
58
|
+
value,
|
|
59
|
+
...rest
|
|
60
|
+
}) {
|
|
61
|
+
const [isOver, setIsOver] = useState(false);
|
|
62
|
+
|
|
63
|
+
const inputValue = useMemo(() => {
|
|
64
|
+
let ret;
|
|
65
|
+
|
|
66
|
+
switch (type) {
|
|
67
|
+
case 'checkbox':
|
|
68
|
+
ret = value || false;
|
|
69
|
+
break;
|
|
70
|
+
case 'bool':
|
|
71
|
+
ret = value;
|
|
72
|
+
break;
|
|
73
|
+
case 'number':
|
|
74
|
+
ret = isUndefined(value) ? '' : value;
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
ret = value || '';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return ret;
|
|
81
|
+
}, [type, value]);
|
|
82
|
+
|
|
83
|
+
const allInputs = useRef(Object.assign(inputs, customInputs));
|
|
84
|
+
const InputComponent = allInputs.current[type] || UnknownInput;
|
|
85
|
+
const inputId = useMemo(() => id || name, [id, name]);
|
|
86
|
+
const descriptionId = `description-${inputId}`;
|
|
87
|
+
const errorId = `error-${inputId}`;
|
|
88
|
+
const handleMouseEvent = () => {
|
|
89
|
+
setIsOver(prev => !prev);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
if (get(customInputs, type, null) !== null) {
|
|
93
|
+
return (
|
|
94
|
+
<InputComponent
|
|
95
|
+
description={description}
|
|
96
|
+
error={inputError}
|
|
97
|
+
label={label}
|
|
98
|
+
name={name}
|
|
99
|
+
onBlur={handleBlur}
|
|
100
|
+
onChange={onChange}
|
|
101
|
+
type={type}
|
|
102
|
+
validations={validations}
|
|
103
|
+
value={value}
|
|
104
|
+
{...rest}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Error
|
|
111
|
+
inputError={inputError}
|
|
112
|
+
name={name}
|
|
113
|
+
translatedErrors={translatedErrors}
|
|
114
|
+
type={type}
|
|
115
|
+
validations={validations}
|
|
116
|
+
>
|
|
117
|
+
{({ canCheck, onBlur, error, dispatch }) => (
|
|
118
|
+
<Wrapper error={error}>
|
|
119
|
+
{type !== 'checkbox' && (
|
|
120
|
+
<Label htmlFor={inputId}>
|
|
121
|
+
<span>
|
|
122
|
+
{label}
|
|
123
|
+
{isEmpty(label) && <> </>}
|
|
124
|
+
</span>
|
|
125
|
+
{rest.labelIcon && (
|
|
126
|
+
<>
|
|
127
|
+
<IconWrapper
|
|
128
|
+
data-tip={rest.labelIcon.title}
|
|
129
|
+
data-for="icon-title"
|
|
130
|
+
onMouseEnter={handleMouseEvent}
|
|
131
|
+
onMouseLeave={handleMouseEvent}
|
|
132
|
+
>
|
|
133
|
+
{rest.labelIcon.icon}
|
|
134
|
+
</IconWrapper>
|
|
135
|
+
{isOver && <Tooltip id="icon-title" />}
|
|
136
|
+
</>
|
|
137
|
+
)}
|
|
138
|
+
</Label>
|
|
139
|
+
)}
|
|
140
|
+
<InputComponent
|
|
141
|
+
{...rest}
|
|
142
|
+
message={label} // Only for the checkbox
|
|
143
|
+
name={name}
|
|
144
|
+
id={inputId}
|
|
145
|
+
aria-describedby={`${!error && description ? descriptionId : ''} ${
|
|
146
|
+
error ? errorId : ''
|
|
147
|
+
}`}
|
|
148
|
+
aria-invalid={error ? 'true' : 'false'}
|
|
149
|
+
onBlur={isFunction(handleBlur) ? handleBlur : onBlur}
|
|
150
|
+
onChange={e => {
|
|
151
|
+
if (!canCheck) {
|
|
152
|
+
dispatch({
|
|
153
|
+
type: 'SET_CHECK',
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
dispatch({
|
|
158
|
+
type: 'SET_ERROR',
|
|
159
|
+
error: null,
|
|
160
|
+
});
|
|
161
|
+
onChange(e);
|
|
162
|
+
}}
|
|
163
|
+
type={type}
|
|
164
|
+
value={inputValue}
|
|
165
|
+
/>
|
|
166
|
+
{!error && description && (
|
|
167
|
+
<Description id={descriptionId} title={description}>
|
|
168
|
+
{description}
|
|
169
|
+
</Description>
|
|
170
|
+
)}
|
|
171
|
+
{error && <ErrorMessage id={errorId}>{error}</ErrorMessage>}
|
|
172
|
+
</Wrapper>
|
|
173
|
+
)}
|
|
174
|
+
</Error>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
Inputs.defaultProps = {
|
|
179
|
+
customInputs: null,
|
|
180
|
+
description: null,
|
|
181
|
+
id: null,
|
|
182
|
+
error: null,
|
|
183
|
+
label: null,
|
|
184
|
+
labelIcon: null,
|
|
185
|
+
onBlur: null,
|
|
186
|
+
onChange: () => {},
|
|
187
|
+
translatedErrors: {
|
|
188
|
+
date: 'This is not a date',
|
|
189
|
+
email: 'This is not an email',
|
|
190
|
+
string: 'This is not a string',
|
|
191
|
+
number: 'This is not a number',
|
|
192
|
+
json: 'This is not a JSON',
|
|
193
|
+
max: 'This is too high',
|
|
194
|
+
maxLength: 'This is too long',
|
|
195
|
+
min: 'This is too small',
|
|
196
|
+
minLength: 'This is too short',
|
|
197
|
+
required: 'This value is required',
|
|
198
|
+
regex: 'This does not match the format',
|
|
199
|
+
uppercase: 'This must be a upper case string',
|
|
200
|
+
},
|
|
201
|
+
validations: {},
|
|
202
|
+
value: null,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
Inputs.propTypes = {
|
|
206
|
+
customInputs: PropTypes.object,
|
|
207
|
+
description: PropTypes.string,
|
|
208
|
+
error: PropTypes.string,
|
|
209
|
+
id: PropTypes.string,
|
|
210
|
+
label: PropTypes.string,
|
|
211
|
+
labelIcon: PropTypes.shape({ icon: PropTypes.any, title: PropTypes.string }),
|
|
212
|
+
name: PropTypes.string.isRequired,
|
|
213
|
+
onBlur: PropTypes.func,
|
|
214
|
+
onChange: () => {},
|
|
215
|
+
translatedErrors: PropTypes.objectOf(PropTypes.string),
|
|
216
|
+
type: PropTypes.string.isRequired,
|
|
217
|
+
validations: PropTypes.object,
|
|
218
|
+
value: PropTypes.any,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
export default Inputs;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* List
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import PropTypes from 'prop-types';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
List as ListCompo,
|
|
12
|
+
ListHeader,
|
|
13
|
+
Padded,
|
|
14
|
+
} from '@punch-in/buffet-modern-core';
|
|
15
|
+
import { Card } from '@punch-in/buffet-modern';
|
|
16
|
+
|
|
17
|
+
function List({
|
|
18
|
+
title,
|
|
19
|
+
subtitle,
|
|
20
|
+
button,
|
|
21
|
+
isLoading,
|
|
22
|
+
items,
|
|
23
|
+
customRowComponent,
|
|
24
|
+
...props
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<Card {...props}>
|
|
28
|
+
<Padded right left size="md">
|
|
29
|
+
<ListHeader title={title} subtitle={subtitle} button={button} />
|
|
30
|
+
</Padded>
|
|
31
|
+
<ListCompo
|
|
32
|
+
items={items}
|
|
33
|
+
isLoading={isLoading}
|
|
34
|
+
customRowComponent={customRowComponent}
|
|
35
|
+
/>
|
|
36
|
+
</Card>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
List.defaultProps = {
|
|
41
|
+
button: null,
|
|
42
|
+
customRowComponent: null,
|
|
43
|
+
isLoading: false,
|
|
44
|
+
items: [],
|
|
45
|
+
title: null,
|
|
46
|
+
subtitle: null,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
List.propTypes = {
|
|
50
|
+
button: PropTypes.shape({
|
|
51
|
+
color: PropTypes.string,
|
|
52
|
+
icon: PropTypes.bool,
|
|
53
|
+
type: PropTypes.string,
|
|
54
|
+
}),
|
|
55
|
+
customRowComponent: PropTypes.func,
|
|
56
|
+
isLoading: PropTypes.bool,
|
|
57
|
+
items: PropTypes.instanceOf(Array),
|
|
58
|
+
subtitle: PropTypes.string,
|
|
59
|
+
title: PropTypes.string,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default List;
|