@spothero/ui 15.1.0 → 15.1.1
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/package.json +2 -3
- package/styles/Alert/Alert.jsx +0 -45
- package/styles/Alert/Alert.spec.js +0 -85
- package/styles/AutoSuggestInput/AutoSuggestInput.jsx +0 -429
- package/styles/AutoSuggestInput/AutoSuggestInput.spec.js +0 -132
- package/styles/AutoSuggestInput/AutoSuggestItem.jsx +0 -61
- package/styles/AutoSuggestInput/AutoSuggestList.jsx +0 -85
- package/styles/Badge/Badge.jsx +0 -24
- package/styles/Badge/Badge.spec.js +0 -43
- package/styles/Chart/Chart.jsx +0 -185
- package/styles/Chart/Chart.spec.js +0 -369
- package/styles/Checkbox/Checkbox.jsx +0 -159
- package/styles/Checkbox/Checkbox.spec.js +0 -142
- package/styles/DateTime/DatePicker.jsx +0 -281
- package/styles/DateTime/DatePicker.spec.js +0 -186
- package/styles/DateTime/DatePickerCalendar.jsx +0 -170
- package/styles/DateTime/DatePickerCalendarNavigation.jsx +0 -44
- package/styles/DateTime/DatePickerCalendarWithRange.jsx +0 -218
- package/styles/DateTime/DateTimePicker.jsx +0 -266
- package/styles/DateTime/DateTimePicker.spec.js +0 -60
- package/styles/DateTime/DateTimeRangePicker.jsx +0 -629
- package/styles/DateTime/DateTimeRangePicker.spec.js +0 -425
- package/styles/DateTime/TimePicker.jsx +0 -158
- package/styles/DateTime/TimePicker.spec.js +0 -148
- package/styles/DateTime/date-time-assertions.js +0 -89
- package/styles/DateTime/index.js +0 -6
- package/styles/ErrorBoundary/ErrorBoundary.jsx +0 -76
- package/styles/ErrorBoundary/ErrorBoundary.spec.js +0 -72
- package/styles/Flyout/Flyout.jsx +0 -147
- package/styles/Flyout/Flyout.spec.js +0 -117
- package/styles/Form/Form.jsx +0 -151
- package/styles/Form/Form.spec.js +0 -148
- package/styles/Form/FormElementError.jsx +0 -18
- package/styles/Form/FormGroup.jsx +0 -32
- package/styles/Form/FormGroupError.jsx +0 -24
- package/styles/Form/index.js +0 -4
- package/styles/GooglePlacesSearchInput/GooglePlacesSearchInput.jsx +0 -215
- package/styles/GooglePlacesSearchInput/GooglePlacesSearchInput.spec.js +0 -213
- package/styles/GooglePlacesSearchInput/PoweredByGoogle.jsx +0 -43
- package/styles/GooglePlacesSearchInput/index.js +0 -2
- package/styles/HorizontalRule/HorizontalRule.jsx +0 -36
- package/styles/HorizontalRule/HorizontalRule.spec.js +0 -94
- package/styles/Label/Label.jsx +0 -22
- package/styles/Label/Label.spec.js +0 -11
- package/styles/Notification/Notification.jsx +0 -117
- package/styles/Notification/Notification.spec.js +0 -154
- package/styles/Notification/NotificationContainer.jsx +0 -90
- package/styles/Notification/NotificationPropTypes.js +0 -20
- package/styles/Notification/index.js +0 -2
- package/styles/PasswordControl/PasswordControl.jsx +0 -197
- package/styles/PasswordControl/PasswordControl.spec.js +0 -236
- package/styles/Portal/Portal.jsx +0 -65
- package/styles/Portal/Portal.spec.js +0 -45
- package/styles/PulseLoader/PulseLoader.jsx +0 -71
- package/styles/PulseLoader/PulseLoader.spec.js +0 -63
- package/styles/Radio/Radio.jsx +0 -114
- package/styles/Radio/Radio.spec.js +0 -128
- package/styles/Radio/RadioGroup.jsx +0 -105
- package/styles/Radio/index.js +0 -2
- package/styles/RenderInBody/RenderInBody.jsx +0 -56
- package/styles/RenderInBody/RenderInBody.spec.js +0 -24
- package/styles/Select/Select.jsx +0 -251
- package/styles/Select/Select.spec.js +0 -254
- package/styles/Select/SelectItemPropTypes.js +0 -19
- package/styles/Select/index.js +0 -2
- package/styles/SelectControlled/SelectControlled.jsx +0 -250
- package/styles/SelectControlled/SelectControlled.spec.js +0 -290
- package/styles/SelectControlled/index.js +0 -1
- package/styles/Sprite/Sprite.jsx +0 -16
- package/styles/Sprite/Sprite.spec.js +0 -11
- package/styles/Tabs/Tab.jsx +0 -38
- package/styles/Tabs/TabContent.jsx +0 -46
- package/styles/Tabs/TabNavigation.jsx +0 -64
- package/styles/Tabs/TabPanel.jsx +0 -30
- package/styles/Tabs/Tabs.jsx +0 -87
- package/styles/Tabs/Tabs.spec.js +0 -201
- package/styles/Tabs/index.js +0 -5
- package/styles/TextArea/TextArea.jsx +0 -137
- package/styles/TextArea/TextArea.spec.js +0 -111
- package/styles/TextInput/TextInput.jsx +0 -159
- package/styles/TextInput/TextInput.spec.js +0 -263
- package/styles/TextInput/TextInputPropTypes.js +0 -88
- package/styles/TextInput/index.js +0 -2
- package/styles/Tooltip/Tooltip.jsx +0 -230
- package/styles/Tooltip/Tooltip.spec.js +0 -170
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import flatten from 'lodash/flatten';
|
|
2
|
-
import forEach from 'lodash/forEach';
|
|
3
|
-
import isBoolean from 'lodash/isBoolean';
|
|
4
|
-
import {barAndLineData, pieAndDonutData} from './stories/helpers/chart-data';
|
|
5
|
-
|
|
6
|
-
describe('<Chart />', () => {
|
|
7
|
-
context('Display', () => {
|
|
8
|
-
const confirmLabelText = ({labelIndex, labelText}) => {
|
|
9
|
-
return cy
|
|
10
|
-
.get('.ct-label')
|
|
11
|
-
.eq(labelIndex)
|
|
12
|
-
.should('contain', labelText);
|
|
13
|
-
};
|
|
14
|
-
const confirmValue = ({className, elementIndex, value}) => {
|
|
15
|
-
return cy
|
|
16
|
-
.get(className)
|
|
17
|
-
.eq(elementIndex)
|
|
18
|
-
.should('have.attr', 'ct:value', `${value}`);
|
|
19
|
-
};
|
|
20
|
-
const confirmTooltipText = ({className, elementIndex, text}) => {
|
|
21
|
-
return cy
|
|
22
|
-
.get(className)
|
|
23
|
-
.eq(elementIndex)
|
|
24
|
-
.trigger('mouseover')
|
|
25
|
-
.wait(100)
|
|
26
|
-
.get('.chartist-tooltip')
|
|
27
|
-
.should('contain', text);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
it('Renders Bar chart correctly', () => {
|
|
31
|
-
cy.visitStory('v1/Chart/Display', 'Default')
|
|
32
|
-
// verify Chart exists
|
|
33
|
-
.get('.Chart')
|
|
34
|
-
.should('have.class', 'ct-chart')
|
|
35
|
-
.should('have.class', 'BarChart')
|
|
36
|
-
// verify it is a chartist Bar chart
|
|
37
|
-
.get('svg')
|
|
38
|
-
.should('have.class', 'ct-chart-bar')
|
|
39
|
-
// verify grids and labels
|
|
40
|
-
.get('.ct-grids')
|
|
41
|
-
.get('.ct-labels')
|
|
42
|
-
// verify label text
|
|
43
|
-
.then(() => {
|
|
44
|
-
forEach(barAndLineData.labels, (labelText, labelIndex) => {
|
|
45
|
-
confirmLabelText({labelIndex, labelText});
|
|
46
|
-
});
|
|
47
|
-
})
|
|
48
|
-
// verify bar values
|
|
49
|
-
.then(() => {
|
|
50
|
-
forEach(barAndLineData.series, series => {
|
|
51
|
-
forEach(series, (value, elementIndex) => {
|
|
52
|
-
confirmValue({
|
|
53
|
-
className: '.ct-bar',
|
|
54
|
-
elementIndex,
|
|
55
|
-
value: `${value}`,
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('Renders Line chart correctly', () => {
|
|
63
|
-
cy.visitStory('v1/Chart/Display', 'LineChart')
|
|
64
|
-
// verify Chart exists
|
|
65
|
-
.get('.Chart')
|
|
66
|
-
.should('have.class', 'ct-chart')
|
|
67
|
-
.should('have.class', 'LineChart')
|
|
68
|
-
// verify it is a chartist Line chart
|
|
69
|
-
.get('svg')
|
|
70
|
-
.should('have.class', 'ct-chart-line')
|
|
71
|
-
// verify grids and labels
|
|
72
|
-
.get('.ct-grids')
|
|
73
|
-
.get('.ct-labels')
|
|
74
|
-
// verify label text
|
|
75
|
-
.then(() => {
|
|
76
|
-
forEach(barAndLineData.labels, (labelText, labelIndex) => {
|
|
77
|
-
confirmLabelText({labelIndex, labelText});
|
|
78
|
-
});
|
|
79
|
-
})
|
|
80
|
-
// verify line point values
|
|
81
|
-
.then(() => {
|
|
82
|
-
forEach(barAndLineData.series, series => {
|
|
83
|
-
forEach(series, (value, elementIndex) => {
|
|
84
|
-
confirmValue({
|
|
85
|
-
className: '.ct-point',
|
|
86
|
-
elementIndex,
|
|
87
|
-
value: `${value}`,
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('Renders Pie chart correctly', () => {
|
|
95
|
-
cy.visitStory('v1/Chart/Display', 'PieChart')
|
|
96
|
-
// verify Chart exists
|
|
97
|
-
.get('.Chart')
|
|
98
|
-
.should('have.class', 'ct-chart')
|
|
99
|
-
.should('have.class', 'PieChart')
|
|
100
|
-
// verify it is a chartist Pie chart
|
|
101
|
-
.get('svg')
|
|
102
|
-
.should('have.class', 'ct-chart-pie')
|
|
103
|
-
// verify grids and labels don't exist
|
|
104
|
-
.get('.ct-grids')
|
|
105
|
-
.should('not.exist')
|
|
106
|
-
.get('.ct-labels')
|
|
107
|
-
.should('not.exist')
|
|
108
|
-
// labels exist outside .ct-labels -- verify label text
|
|
109
|
-
.then(() => {
|
|
110
|
-
forEach(pieAndDonutData.labels, (labelText, labelIndex) => {
|
|
111
|
-
confirmLabelText({labelIndex, labelText});
|
|
112
|
-
});
|
|
113
|
-
})
|
|
114
|
-
// verify pie slice values
|
|
115
|
-
.then(() => {
|
|
116
|
-
forEach(pieAndDonutData.series, (value, elementIndex) => {
|
|
117
|
-
confirmValue({
|
|
118
|
-
className: '.ct-slice-pie',
|
|
119
|
-
elementIndex,
|
|
120
|
-
value: `${value}`,
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('Renders Donut chart correctly', () => {
|
|
127
|
-
cy.visitStory('v1/Chart/Display', 'DonutChart')
|
|
128
|
-
// verify Chart exists
|
|
129
|
-
.get('.Chart')
|
|
130
|
-
.should('have.class', 'ct-chart')
|
|
131
|
-
.should('have.class', 'DonutChart')
|
|
132
|
-
// verify it is a chartist Donut chart
|
|
133
|
-
.get('svg')
|
|
134
|
-
.should('have.class', 'ct-chart-donut')
|
|
135
|
-
// verify grids and labels don't exist
|
|
136
|
-
.get('.ct-grids')
|
|
137
|
-
.should('not.exist')
|
|
138
|
-
.get('.ct-labels')
|
|
139
|
-
.should('not.exist')
|
|
140
|
-
// labels exist outside .ct-labels -- verify label text
|
|
141
|
-
.then(() => {
|
|
142
|
-
forEach(pieAndDonutData.labels, (labelText, labelIndex) => {
|
|
143
|
-
confirmLabelText({labelIndex, labelText});
|
|
144
|
-
});
|
|
145
|
-
})
|
|
146
|
-
// verify donut piece values
|
|
147
|
-
.then(() => {
|
|
148
|
-
forEach(pieAndDonutData.series, (value, elementIndex) => {
|
|
149
|
-
confirmValue({
|
|
150
|
-
className: '.ct-slice-donut',
|
|
151
|
-
elementIndex,
|
|
152
|
-
value: `${value}`,
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('Renders chart with tooltips - set via boolean', () => {
|
|
159
|
-
cy.visitStory('v1/Chart/Display', 'WithTooltipBoolean')
|
|
160
|
-
// verify tooltip exists and is not showing
|
|
161
|
-
.get('.chartist-tooltip')
|
|
162
|
-
.should('have.css', 'opacity', '0')
|
|
163
|
-
// verify tooltips
|
|
164
|
-
.then(() => {
|
|
165
|
-
forEach(barAndLineData.series, series => {
|
|
166
|
-
forEach(series, (value, elementIndex) => {
|
|
167
|
-
confirmTooltipText({
|
|
168
|
-
className: '.ct-bar',
|
|
169
|
-
elementIndex,
|
|
170
|
-
text: `${value}`,
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('Renders chart with tooltips - set via object', () => {
|
|
178
|
-
cy.visitStory('v1/Chart/Display', 'WithTooltipObject', {
|
|
179
|
-
onBeforeLoad: contentWindow => {
|
|
180
|
-
contentWindow.tooltipFnc = (label, value) => {
|
|
181
|
-
console.log('tooltipFnc'); // eslint-disable-line no-console
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
cy.stub(contentWindow, 'tooltipFnc').as('tooltipFnc');
|
|
185
|
-
},
|
|
186
|
-
})
|
|
187
|
-
// verify tooltip exists and is not showing
|
|
188
|
-
.get('.chartist-tooltip')
|
|
189
|
-
.should('have.css', 'opacity', '0')
|
|
190
|
-
// verify tooltips
|
|
191
|
-
.then(() => {
|
|
192
|
-
forEach(barAndLineData.series, series => {
|
|
193
|
-
forEach(series, (value, elementIndex) => {
|
|
194
|
-
confirmTooltipText({
|
|
195
|
-
className: '.ct-bar',
|
|
196
|
-
elementIndex,
|
|
197
|
-
text: `${value} Projects`,
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
})
|
|
202
|
-
// verify tooltipFnc function was called 5 times, once per tooltip
|
|
203
|
-
.get('@tooltipFnc')
|
|
204
|
-
.should(
|
|
205
|
-
'have.callCount',
|
|
206
|
-
flatten(barAndLineData.series).length
|
|
207
|
-
);
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('Renders chart with legend - set via boolean', () => {
|
|
211
|
-
cy.visitStory('v1/Chart/Display', 'WithLegendBoolean')
|
|
212
|
-
// verify legend exists and has default text (the data values)
|
|
213
|
-
.get('.ct-legend')
|
|
214
|
-
.should('contain', '10,21,32,46,12');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('Renders chart with legend - set via object', () => {
|
|
218
|
-
cy.visitStory('v1/Chart/Display', 'WithLegendObject')
|
|
219
|
-
// verify legend exists and has custom legend name set via legend object
|
|
220
|
-
.get('.ct-legend')
|
|
221
|
-
.should('contain', 'Number of Projects');
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
it('Renders chart with responsive options', () => {
|
|
225
|
-
cy.visitStory('v1/Chart/Display', 'WithResponsiveOptions')
|
|
226
|
-
.viewport('macbook-15')
|
|
227
|
-
// verify horizontal positioned and vertical positioned grid lines exist
|
|
228
|
-
.get('.ct-grid.ct-horizontal')
|
|
229
|
-
.get('.ct-grid.ct-vertical')
|
|
230
|
-
// go to a smaller viewport and verify horizontal positioned grid lines do not exist
|
|
231
|
-
.viewport('iphone-6')
|
|
232
|
-
.get('.ct-grid.ct-horizontal')
|
|
233
|
-
.should('not.exist');
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('Renders chart with square aspect ratio', () => {
|
|
237
|
-
cy.visitStory('v1/Chart/Display', 'WithAspectRatio')
|
|
238
|
-
// verify chart has square aspect ratio class
|
|
239
|
-
.get('.Chart')
|
|
240
|
-
.should('have.class', 'ct-chart')
|
|
241
|
-
.should('have.class', 'ct-square');
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
context('Events', () => {
|
|
246
|
-
it('All events and handlers function properly', () => {
|
|
247
|
-
cy.visitStory('v1/Chart/Events', 'Default', {
|
|
248
|
-
onBeforeLoad: contentWindow => {
|
|
249
|
-
contentWindow.onDataHandler = chartData => {
|
|
250
|
-
console.log('onDataHandler'); // eslint-disable-line no-console
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
contentWindow.onCreatedHandler = chartData => {
|
|
254
|
-
console.log('onCreatedHandler'); // eslint-disable-line no-console
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
contentWindow.onDrawHandler = chartData => {
|
|
258
|
-
console.log('onDrawHandler'); // eslint-disable-line no-console
|
|
259
|
-
};
|
|
260
|
-
|
|
261
|
-
contentWindow.onOptionsChangedHandler = chartData => {
|
|
262
|
-
console.log('onOptionsChangedHandler'); // eslint-disable-line no-console
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
contentWindow.onAnimationBeginHandler = chartData => {
|
|
266
|
-
console.log('onAnimationBeginHandler'); // eslint-disable-line no-console
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
contentWindow.onAnimationEndHandler = chartData => {
|
|
270
|
-
console.log('onAnimationEndHandler'); // eslint-disable-line no-console
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
cy.stub(contentWindow, 'onDrawHandler').as('drawHandler');
|
|
274
|
-
|
|
275
|
-
cy.stub(contentWindow, 'onCreatedHandler').as(
|
|
276
|
-
'createdHandler'
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
cy.stub(contentWindow, 'onDataHandler').as('dataHandler');
|
|
280
|
-
|
|
281
|
-
cy.stub(contentWindow, 'onOptionsChangedHandler').as(
|
|
282
|
-
'optionsChangedHandler'
|
|
283
|
-
);
|
|
284
|
-
|
|
285
|
-
cy.stub(contentWindow, 'onAnimationBeginHandler').as(
|
|
286
|
-
'animationBeginHandler'
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
cy.stub(contentWindow, 'onAnimationEndHandler').as(
|
|
290
|
-
'animationEndHandler'
|
|
291
|
-
);
|
|
292
|
-
},
|
|
293
|
-
})
|
|
294
|
-
.viewport('macbook-15')
|
|
295
|
-
.wait(500)
|
|
296
|
-
// verify data event fired
|
|
297
|
-
.get('@dataHandler')
|
|
298
|
-
.should('be.calledWith', {
|
|
299
|
-
type: 'initial',
|
|
300
|
-
data: barAndLineData,
|
|
301
|
-
})
|
|
302
|
-
// verify proper draw events fired
|
|
303
|
-
.get('@drawHandler')
|
|
304
|
-
.should('be.calledWithMatch', chartData => {
|
|
305
|
-
return chartData.type && chartData.type === 'label';
|
|
306
|
-
})
|
|
307
|
-
.should('be.calledWithMatch', chartData => {
|
|
308
|
-
return chartData.type && chartData.type === 'grid';
|
|
309
|
-
})
|
|
310
|
-
.should('be.calledWithMatch', chartData => {
|
|
311
|
-
return chartData.type && chartData.type === 'point';
|
|
312
|
-
})
|
|
313
|
-
.should('be.calledWithMatch', chartData => {
|
|
314
|
-
return chartData.type && chartData.type === 'line';
|
|
315
|
-
})
|
|
316
|
-
.should('be.calledWithMatch', chartData => {
|
|
317
|
-
return chartData.type && chartData.type === 'area';
|
|
318
|
-
})
|
|
319
|
-
// verify created event fired
|
|
320
|
-
.get('@createdHandler')
|
|
321
|
-
.should('be.calledWithMatch', chartData => {
|
|
322
|
-
const options = chartData.options || false;
|
|
323
|
-
|
|
324
|
-
return options
|
|
325
|
-
? isBoolean(options.fullWidth) &&
|
|
326
|
-
options.fullWidth === true &&
|
|
327
|
-
isBoolean(options.showArea) &&
|
|
328
|
-
options.showArea === true
|
|
329
|
-
: false;
|
|
330
|
-
})
|
|
331
|
-
// verify animation begin event fired
|
|
332
|
-
.get('@animationBeginHandler')
|
|
333
|
-
.should('be.calledWithMatch', chartData => {
|
|
334
|
-
const params = chartData.params || false;
|
|
335
|
-
|
|
336
|
-
return params
|
|
337
|
-
? params.dur && params.dur === '300ms'
|
|
338
|
-
: false;
|
|
339
|
-
})
|
|
340
|
-
// verify animation end event fired
|
|
341
|
-
.get('@animationEndHandler')
|
|
342
|
-
.should('be.calledWithMatch', chartData => {
|
|
343
|
-
const params = chartData.params || false;
|
|
344
|
-
|
|
345
|
-
return params
|
|
346
|
-
? params.dur && params.dur === '300ms'
|
|
347
|
-
: false;
|
|
348
|
-
})
|
|
349
|
-
// now change viewport to test options changed event (due to responsive options trigger)
|
|
350
|
-
.viewport('iphone-6')
|
|
351
|
-
// verify animation end event fired
|
|
352
|
-
.get('@optionsChangedHandler')
|
|
353
|
-
.should('be.calledWithMatch', chartData => {
|
|
354
|
-
const {currentOptions, previousOptions} = chartData;
|
|
355
|
-
const currAxisX = currentOptions.axisX;
|
|
356
|
-
const prevAxisX = previousOptions.axisX;
|
|
357
|
-
|
|
358
|
-
return (
|
|
359
|
-
currAxisX &&
|
|
360
|
-
isBoolean(currAxisX.showGrid) &&
|
|
361
|
-
currAxisX.showGrid === false &&
|
|
362
|
-
prevAxisX &&
|
|
363
|
-
isBoolean(prevAxisX.showGrid) &&
|
|
364
|
-
prevAxisX.showGrid === true
|
|
365
|
-
);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
});
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import React, {PureComponent, cloneElement} from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import classNames from 'classnames';
|
|
4
|
-
import IconCheck from '@spothero/icons/check';
|
|
5
|
-
|
|
6
|
-
export default class Checkbox extends PureComponent {
|
|
7
|
-
static propTypes = {
|
|
8
|
-
/** Additional class(es) to add to the component. */
|
|
9
|
-
className: PropTypes.string,
|
|
10
|
-
/** A unique ID which is used to identify this element for accessibility purposes on the client as well as the server. */
|
|
11
|
-
id: PropTypes.string.isRequired,
|
|
12
|
-
/** A unique name which is used to identify this element. If belonging to a form this is used as the key for serialization and is required. */
|
|
13
|
-
name: PropTypes.string,
|
|
14
|
-
/** The value to apply this element. If belonging to a form this is used as the value for serialization and is required. */
|
|
15
|
-
value: PropTypes.string,
|
|
16
|
-
/** The icon to be used in the checkbox. */
|
|
17
|
-
icon: PropTypes.node,
|
|
18
|
-
/** A custom label (typically a Label component) to display next to the checkbox. This is required if the `toggle` prop is false. */
|
|
19
|
-
label: PropTypes.element,
|
|
20
|
-
/** Whether this field is required during validation. */
|
|
21
|
-
required: PropTypes.bool,
|
|
22
|
-
/** Whether the checkbox should be checked on mount. */
|
|
23
|
-
defaultChecked: PropTypes.bool,
|
|
24
|
-
/** A custom validation error (typically a FormElementError component) to display when this component is part of a form and is invalid. */
|
|
25
|
-
error: PropTypes.element,
|
|
26
|
-
/** Disallows user interaction. */
|
|
27
|
-
disabled: PropTypes.bool,
|
|
28
|
-
/** Whether to render the checkbox as a toggle switch. Labels will be ignored. */
|
|
29
|
-
toggle: PropTypes.bool,
|
|
30
|
-
/**
|
|
31
|
-
* Function to execute when the checked state changes.
|
|
32
|
-
*
|
|
33
|
-
* @param {SyntheticEvent} evt - The React `SyntheticEvent`.
|
|
34
|
-
*/
|
|
35
|
-
onChange: PropTypes.func,
|
|
36
|
-
/**
|
|
37
|
-
* Function to execute when the element is marked as invalid by a validation check.
|
|
38
|
-
*
|
|
39
|
-
* @param {ValidityState} validationState - ValidityState object for more fine grained control over error handling in a parent component if desired.
|
|
40
|
-
*/
|
|
41
|
-
onInvalid: PropTypes.func,
|
|
42
|
-
};
|
|
43
|
-
static defaultProps = {
|
|
44
|
-
value: 'on',
|
|
45
|
-
icon: <IconCheck />,
|
|
46
|
-
};
|
|
47
|
-
state = {
|
|
48
|
-
validationState: null,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
constructor(props) {
|
|
52
|
-
super(props);
|
|
53
|
-
|
|
54
|
-
const {id, toggle, label} = props;
|
|
55
|
-
|
|
56
|
-
if (!id) {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`The prop 'id' is required to make Checkbox accessible for users using assistive technologies such as screen readers. Check the render method of Checkbox.`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (!toggle && !label) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`The label prop is required when using a Checkbox that isn't a toggle.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
componentDidMount() {
|
|
70
|
-
this._input.addEventListener('invalid', this._onInvalid);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
componentWillUnmount() {
|
|
74
|
-
this._input.removeEventListener('invalid', this._onInvalid);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
_onInvalid = evt => {
|
|
78
|
-
evt.preventDefault();
|
|
79
|
-
|
|
80
|
-
const {onInvalid} = this.props;
|
|
81
|
-
const validationState = evt.currentTarget.validity;
|
|
82
|
-
|
|
83
|
-
this.setState({
|
|
84
|
-
validationState,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
if (onInvalid) {
|
|
88
|
-
onInvalid(validationState);
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
_onChange = evt => {
|
|
93
|
-
const {onChange} = this.props;
|
|
94
|
-
|
|
95
|
-
this.setState({
|
|
96
|
-
validationState: null,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (onChange) {
|
|
100
|
-
onChange(evt);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
_renderIcon() {
|
|
105
|
-
return this.props.icon;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
render() {
|
|
109
|
-
const {
|
|
110
|
-
label,
|
|
111
|
-
className,
|
|
112
|
-
name,
|
|
113
|
-
id,
|
|
114
|
-
value,
|
|
115
|
-
defaultChecked,
|
|
116
|
-
required,
|
|
117
|
-
disabled,
|
|
118
|
-
toggle,
|
|
119
|
-
error,
|
|
120
|
-
} = this.props;
|
|
121
|
-
const {validationState} = this.state;
|
|
122
|
-
const isValid = validationState ? validationState.valid : true;
|
|
123
|
-
const classes = classNames(
|
|
124
|
-
'Checkbox',
|
|
125
|
-
{'Checkbox-toggle': toggle},
|
|
126
|
-
'FormElement',
|
|
127
|
-
{'FormElement-contains-error': !isValid},
|
|
128
|
-
className
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<div className={classes}>
|
|
133
|
-
<div className="FormElement-control">
|
|
134
|
-
<input
|
|
135
|
-
ref={node => {
|
|
136
|
-
this._input = node;
|
|
137
|
-
}}
|
|
138
|
-
id={`Checkbox-${id}`}
|
|
139
|
-
className="FormElement-item"
|
|
140
|
-
type="checkbox"
|
|
141
|
-
name={name}
|
|
142
|
-
defaultChecked={defaultChecked}
|
|
143
|
-
required={required}
|
|
144
|
-
disabled={disabled}
|
|
145
|
-
value={value}
|
|
146
|
-
onChange={this._onChange}
|
|
147
|
-
/>
|
|
148
|
-
{this._renderIcon()}
|
|
149
|
-
{!toggle &&
|
|
150
|
-
cloneElement(label, {
|
|
151
|
-
htmlFor: `Checkbox-${id}`,
|
|
152
|
-
})}
|
|
153
|
-
{toggle && <span className="Label" />}
|
|
154
|
-
</div>
|
|
155
|
-
{!isValid && error}
|
|
156
|
-
</div>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
describe('<Checkbox />', () => {
|
|
2
|
-
context('Display', () => {
|
|
3
|
-
context('Default', () => {
|
|
4
|
-
it('Should have an input and some kind of label element', () => {
|
|
5
|
-
cy.visitStory('v1/Checkbox/Display', 'Default')
|
|
6
|
-
.get('.Checkbox')
|
|
7
|
-
.find('input[type=checkbox]')
|
|
8
|
-
.should('have.class', 'FormElement-item')
|
|
9
|
-
.and('not.have.attr', 'disabled')
|
|
10
|
-
.get('.Checkbox')
|
|
11
|
-
.find('.Label')
|
|
12
|
-
.contains('Default');
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
context('Checked', () => {
|
|
17
|
-
it('Should be checked by default', () => {
|
|
18
|
-
cy.visitStory('v1/Checkbox/Display', 'Checked')
|
|
19
|
-
.get('.Checkbox')
|
|
20
|
-
.find('input[type=checkbox]')
|
|
21
|
-
.should('be.checked');
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
context('Disabled', () => {
|
|
26
|
-
it('Should have a disabled input', () => {
|
|
27
|
-
cy.visitStory('v1/Checkbox/Display', 'Disabled')
|
|
28
|
-
.get('.Checkbox')
|
|
29
|
-
.find('input[type=checkbox]')
|
|
30
|
-
.should('be.disabled');
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
context('Required', () => {
|
|
35
|
-
it('Should have a required input', () => {
|
|
36
|
-
cy.visitStory('v1/Checkbox/Display', 'Required')
|
|
37
|
-
.get('.Checkbox')
|
|
38
|
-
.find('input[type=checkbox]')
|
|
39
|
-
.should('have.attr', 'required');
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
context('With Icon', () => {
|
|
44
|
-
it('Should have an icon', () => {
|
|
45
|
-
cy.visitStory('v1/Checkbox/Display', 'Icon')
|
|
46
|
-
.get('.Checkbox')
|
|
47
|
-
.find('.Icon-info')
|
|
48
|
-
.should('exist');
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
context('With Props', () => {
|
|
53
|
-
it('Should have a custom class name', () => {
|
|
54
|
-
cy.visitStory('v1/Checkbox/Display', 'WithProps')
|
|
55
|
-
.get('.Checkbox')
|
|
56
|
-
.should('have.class', 'Checkbox-class');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('Should have a name', () => {
|
|
60
|
-
cy.visitStory('v1/Checkbox/Display', 'WithProps')
|
|
61
|
-
.get('.Checkbox')
|
|
62
|
-
.find('input[type=checkbox]')
|
|
63
|
-
.should('have.attr', 'name');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('Should have a value', () => {
|
|
67
|
-
cy.visitStory('v1/Checkbox/Display', 'WithProps')
|
|
68
|
-
.get('.Checkbox')
|
|
69
|
-
.find('input[type=checkbox]')
|
|
70
|
-
.should('have.value', 'formValue');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('Should have a label', () => {
|
|
74
|
-
cy.visitStory('v1/Checkbox/Display', 'WithProps')
|
|
75
|
-
.get('.Checkbox')
|
|
76
|
-
.find('.Label')
|
|
77
|
-
.contains('With Props');
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
context('Methods', () => {
|
|
83
|
-
it('All methods function properly on checkboxes', () => {
|
|
84
|
-
cy.visitStory('v1/Checkbox/Methods', 'Default', {
|
|
85
|
-
onBeforeLoad: contentWindow => {
|
|
86
|
-
contentWindow.onCheckboxChange = evt => {
|
|
87
|
-
console.log('onCheckboxChange'); // eslint-disable-line no-console
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
contentWindow.onCheckboxInvalid = validationState => {
|
|
91
|
-
console.log('onCheckboxInvalid'); // eslint-disable-line no-console
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
contentWindow.onCheckboxFormSubmit = evt => {
|
|
95
|
-
console.log('onCheckboxFormSubmit'); // eslint-disable-line no-console
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
cy.stub(contentWindow, 'onCheckboxChange').as(
|
|
99
|
-
'checkboxChange'
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
cy.stub(contentWindow, 'onCheckboxInvalid').as(
|
|
103
|
-
'checkboxInvalid'
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
cy.stub(contentWindow, 'onCheckboxFormSubmit').as(
|
|
107
|
-
'checkboxFormSubmit'
|
|
108
|
-
);
|
|
109
|
-
},
|
|
110
|
-
})
|
|
111
|
-
.get('.Checkbox')
|
|
112
|
-
.find('input[type=checkbox]')
|
|
113
|
-
.check()
|
|
114
|
-
// call change on check
|
|
115
|
-
.get('@checkboxChange')
|
|
116
|
-
.should('be.called')
|
|
117
|
-
.get('.Checkbox')
|
|
118
|
-
.find('input[type=checkbox]')
|
|
119
|
-
// call change on uncheck
|
|
120
|
-
.uncheck()
|
|
121
|
-
.get('@checkboxChange')
|
|
122
|
-
.should('be.called')
|
|
123
|
-
// call submit on unchecked required field to trigger error
|
|
124
|
-
.get('.FormElementError')
|
|
125
|
-
.should('not.exist')
|
|
126
|
-
.get('.Form')
|
|
127
|
-
.submit()
|
|
128
|
-
.get('@checkboxInvalid')
|
|
129
|
-
.should('be.called')
|
|
130
|
-
.get('.FormElementError')
|
|
131
|
-
.should('exist')
|
|
132
|
-
// call submit on checked required field to trigger submit
|
|
133
|
-
.get('.Checkbox')
|
|
134
|
-
.find('input[type=checkbox]')
|
|
135
|
-
.check()
|
|
136
|
-
.get('.Form')
|
|
137
|
-
.submit()
|
|
138
|
-
.get('@checkboxFormSubmit')
|
|
139
|
-
.should('be.called');
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
});
|