@configuratorware/configurator-frontendgui 1.57.15 → 1.58.0

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.
@@ -27,6 +27,7 @@ var _ObjectTools = _interopRequireDefault(require("../ObjectTools"));
27
27
  var _AnalyticsWrapper = _interopRequireDefault(require("../../../../Shared/Components/Analytics/AnalyticsWrapper"));
28
28
  var _CustomDialog = _interopRequireDefault(require("../../../../../Shared/Components/CustomDialog"));
29
29
  var _customClassName = _interopRequireDefault(require("../../../../Utils/customClassName"));
30
+ var _MainButton = _interopRequireDefault(require("../../../../../Shared/Components/MainButton"));
30
31
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
31
32
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
32
33
  function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
@@ -67,11 +68,15 @@ var styles = function styles(theme) {
67
68
  width: 50,
68
69
  margin: '15px 5px 0 0'
69
70
  })), _CardContentWrapper),
71
+ printQualityContainer: {
72
+ flexWrap: 'nowrap',
73
+ marginBottom: 15,
74
+ gap: 8
75
+ },
70
76
  printQuality: _defineProperty({
71
77
  display: 'inline-flex',
72
78
  alignItems: 'center',
73
79
  backgroundColor: '#000000',
74
- marginBottom: 15,
75
80
  borderRadius: '4px',
76
81
  color: '#fff',
77
82
  padding: '10px',
@@ -178,7 +183,8 @@ var ImageControlbox = /*#__PURE__*/function (_React$Component) {
178
183
  lg: 12
179
184
  }, /*#__PURE__*/_react["default"].createElement(_Grid["default"], {
180
185
  container: true,
181
- alignItems: "center"
186
+ alignItems: "center",
187
+ className: classes.printQualityContainer
182
188
  }, /*#__PURE__*/_react["default"].createElement(_Grid["default"], {
183
189
  item: true,
184
190
  lg: printQualityInfoExists ? 10 : 12,
@@ -227,12 +233,13 @@ var ImageControlbox = /*#__PURE__*/function (_React$Component) {
227
233
  title: (0, _i18n.t)('imageControlbox.editButtonTooltip')
228
234
  }, /*#__PURE__*/_react["default"].createElement(_Icon["default"], null, "edit")))))))), /*#__PURE__*/_react["default"].createElement(_CustomDialog["default"], {
229
235
  open: printQualityInfoDialogOpen,
236
+ onCancel: this.handlePrintQualityInfoDialogClose,
230
237
  onClose: this.handlePrintQualityInfoDialogClose
231
238
  }, /*#__PURE__*/_react["default"].createElement(_DialogContent["default"], null, /*#__PURE__*/_react["default"].createElement(_DialogContentText["default"], {
232
239
  id: "alert-dialog-description"
233
- }, (0, _i18n.t)('imageControlbox.printQuality.' + printQuality))), /*#__PURE__*/_react["default"].createElement(_DialogActions["default"], null, /*#__PURE__*/_react["default"].createElement(_Button["default"], {
234
- onClick: this.handlePrintQualityInfoDialogClose,
235
- color: "primary"
240
+ }, (0, _i18n.t)('imageControlbox.printQuality.' + printQuality))), /*#__PURE__*/_react["default"].createElement(_DialogActions["default"], null, /*#__PURE__*/_react["default"].createElement(_MainButton["default"], {
241
+ variant: "contained",
242
+ onClick: this.handlePrintQualityInfoDialogClose
236
243
  }, "OK"))));
237
244
  }
238
245
  }]);
@@ -24,7 +24,7 @@ var _Actions = require("../../Reducers/ImageGallery/Actions");
24
24
  var _Selectors3 = require("../../Reducers/UI/Selectors");
25
25
  var _ImageUpload = _interopRequireDefault(require("../../Modules/Designer/Components/ImageEditDialog/ImageUpload"));
26
26
  var _ImageColorPicker = _interopRequireDefault(require("../../Modules/Designer/Components/ImageColorPicker/ImageColorPicker"));
27
- var _Transformers = require("../../Modules/Designer/Utils/Transformers");
27
+ var _DesignDataService = require("../../Services/DesignDataService");
28
28
  var _Progress = _interopRequireDefault(require("../../Shared/Components/Progress/Progress"));
29
29
  var _ThemeProvider = _interopRequireDefault(require("../Configurator/ThemeProvider"));
30
30
  var _DesignerProductPreviewManager = require("./DesignerProductPreviewManager");
@@ -168,37 +168,81 @@ var DesignerProductPreviewScreen = function DesignerProductPreviewScreen(props)
168
168
  (0, _react.useEffect)(function () {
169
169
  setApplied(false);
170
170
  }, [editParams]);
171
- var previewSrc = (0, _react.useMemo)(function () {
172
- return imageData && "".concat((0, _Transformers.getRasterURL)((0, _Selectors2.getPreviewUrlFromImageData)(imageData.preview)), "?").concat(Math.random());
173
- }, [imageData]);
174
- var nextAction = /*#__PURE__*/function () {
175
- var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
171
+ var _useState9 = (0, _react.useState)(null),
172
+ _useState0 = _slicedToArray(_useState9, 2),
173
+ previewSrc = _useState0[0],
174
+ setPreviewSrc = _useState0[1];
175
+ (0, _react.useEffect)(function () {
176
+ var _imageData$preview;
177
+ if (!(imageData !== null && imageData !== void 0 && (_imageData$preview = imageData.preview) !== null && _imageData$preview !== void 0 && _imageData$preview.url)) {
178
+ setPreviewSrc(null);
179
+ return;
180
+ }
181
+ var cancelled = false;
182
+ var previewUrl = (0, _Selectors2.getPreviewUrlFromImageData)(imageData.preview);
183
+ var isSvg = /\.svg$/.test(previewUrl);
184
+ _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
185
+ var svgContent;
176
186
  return _regeneratorRuntime().wrap(function _callee$(_context) {
177
187
  while (1) switch (_context.prev = _context.next) {
178
188
  case 0:
179
- _context.t0 = step;
180
- _context.next = _context.t0 === Steps.upload ? 3 : _context.t0 === Steps.removeBackground ? 5 : 10;
189
+ if (!isSvg) {
190
+ _context.next = 9;
191
+ break;
192
+ }
193
+ _context.next = 3;
194
+ return (0, _DesignDataService.getSvgContent)(previewUrl, true, true);
195
+ case 3:
196
+ svgContent = _context.sent;
197
+ if (!cancelled) {
198
+ _context.next = 6;
199
+ break;
200
+ }
201
+ return _context.abrupt("return");
202
+ case 6:
203
+ setPreviewSrc((0, _DesignDataService.applySvgContentOperations)(imageData.original, imageData.preview, null, svgContent));
204
+ _context.next = 10;
205
+ break;
206
+ case 9:
207
+ setPreviewSrc("".concat(previewUrl, "?").concat(Math.random()));
208
+ case 10:
209
+ case "end":
210
+ return _context.stop();
211
+ }
212
+ }, _callee);
213
+ }))();
214
+ return function () {
215
+ cancelled = true;
216
+ };
217
+ }, [imageData]);
218
+ var nextAction = /*#__PURE__*/function () {
219
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
220
+ return _regeneratorRuntime().wrap(function _callee2$(_context2) {
221
+ while (1) switch (_context2.prev = _context2.next) {
222
+ case 0:
223
+ _context2.t0 = step;
224
+ _context2.next = _context2.t0 === Steps.upload ? 3 : _context2.t0 === Steps.removeBackground ? 5 : 10;
181
225
  break;
182
226
  case 3:
183
227
  imageUploadRef.current.uploadFile();
184
- return _context.abrupt("break", 10);
228
+ return _context2.abrupt("break", 10);
185
229
  case 5:
186
- _context.next = 7;
230
+ _context2.next = 7;
187
231
  return editFile(_objectSpread(_objectSpread({}, editParams), {}, {
188
232
  vectorize: true
189
233
  }), imageData);
190
234
  case 7:
191
235
  manager.useImage();
192
236
  setApplied(true);
193
- return _context.abrupt("break", 10);
237
+ return _context2.abrupt("break", 10);
194
238
  case 10:
195
239
  case "end":
196
- return _context.stop();
240
+ return _context2.stop();
197
241
  }
198
- }, _callee);
242
+ }, _callee2);
199
243
  }));
200
244
  return function nextAction() {
201
- return _ref.apply(this, arguments);
245
+ return _ref2.apply(this, arguments);
202
246
  };
203
247
  }();
204
248
  var updateParams = function updateParams(params) {
@@ -222,8 +266,8 @@ var DesignerProductPreviewScreen = function DesignerProductPreviewScreen(props)
222
266
  clippingColor: editParams.clippingColor || _Constants.defaultClippingColor
223
267
  }));
224
268
  };
225
- var onClippingChange = function onClippingChange(_ref2) {
226
- var clipping = _ref2.target.value;
269
+ var onClippingChange = function onClippingChange(_ref3) {
270
+ var clipping = _ref3.target.value;
227
271
  return updateParams(_objectSpread(_objectSpread({}, editParams), {}, {
228
272
  clipping: clipping
229
273
  }));
@@ -606,7 +606,7 @@ var AmountPrice = /*#__PURE__*/function (_React$Component) {
606
606
  root: classes.AmountPriceButton,
607
607
  sizeSmall: classes.smallButtonSize
608
608
  }
609
- }, amount, " ", (0, _i18n.t)('amountPrice.pieces'))), !hidePrices && /*#__PURE__*/_react["default"].createElement(_Grid["default"], {
609
+ }, amount, ' ', amount === 1 ? (0, _i18n.t)('amountPrice.piece') : (0, _i18n.t)('amountPrice.pieces'))), !hidePrices && /*#__PURE__*/_react["default"].createElement(_Grid["default"], {
610
610
  item: true,
611
611
  xs: 12,
612
612
  sm: 6,
@@ -101,6 +101,19 @@ it('renders without callToAction', function () {
101
101
  }));
102
102
  expect(tree).toMatchSnapshot();
103
103
  });
104
+ it('renders singular piece label when amount is 1', function () {
105
+ var tree = (0, _enzyme.render)( /*#__PURE__*/_react["default"].createElement(_index["default"], {
106
+ width: "lg",
107
+ amount: 1,
108
+ priceFormatted: "500,00 \u20AC",
109
+ vatNotice: "VAT included plus shipping",
110
+ priceList: mockedPriceData,
111
+ onChangeAmount: _noop["default"],
112
+ ConfigurationModeSwitchComponent: getDefaultComponent
113
+ }));
114
+ expect(tree.text()).toContain('1 amountPrice.piece');
115
+ expect(tree.text()).not.toContain('amountPrice.pieces');
116
+ });
104
117
  it('renders receiveOfferForm', function () {
105
118
  var tree = (0, _enzyme.render)( /*#__PURE__*/_react["default"].createElement(_index["default"], {
106
119
  showReceiveOfferForm: true,
@@ -142,7 +142,7 @@ var PriceList = /*#__PURE__*/function (_React$Component) {
142
142
  root: classes.MuiTypographyNoWrap,
143
143
  gutterBottom: classes.gutterBottom
144
144
  }
145
- }, amount, " ", (0, _i18n.t)('amountPrice.pieces')), /*#__PURE__*/_react["default"].createElement(_ListItemText["default"], {
145
+ }, amount, " ", amount === 1 ? (0, _i18n.t)('amountPrice.piece') : (0, _i18n.t)('amountPrice.pieces')), /*#__PURE__*/_react["default"].createElement(_ListItemText["default"], {
146
146
  className: classes.listItemPrimary,
147
147
  primary: productTitle
148
148
  }))), list && list.length > 0 && /*#__PURE__*/_react["default"].createElement(_List["default"], {
@@ -173,10 +173,9 @@ var ProductVariant = /*#__PURE__*/function (_React$Component) {
173
173
  sizes: _objectSpread(_objectSpread({}, state.sizes), {}, _defineProperty({}, size.identifier, value))
174
174
  };
175
175
  });
176
- } else {
177
- if ((0, _isFunction["default"])(_this.props.handleChange)) {
178
- _this.props.handleChange(variant, size, value);
179
- }
176
+ }
177
+ if ((0, _isFunction["default"])(_this.props.handleChange)) {
178
+ _this.props.handleChange(variant, size, value);
180
179
  }
181
180
  };
182
181
  });
@@ -350,9 +349,9 @@ var ProductVariant = /*#__PURE__*/function (_React$Component) {
350
349
  label: classes.ButtonOutlinedRootLabel
351
350
  }
352
351
  }, (0, _i18n.t)('productVariant.confirmSelection'))), (0, _get["default"])(variant, 'sizes.length', 0) === 0 && /*#__PURE__*/_react["default"].createElement(_AmountInput["default"], {
353
- value: variant.amount + ' ' + (0, _i18n.t)('amountSelection.amount'),
354
- amountLabel: (0, _i18n.t)('productVariant.amountLabel'),
352
+ value: variant.amount + ' ' + (variant.amount === 1 ? (0, _i18n.t)('amountSelection.piece') : (0, _i18n.t)('amountSelection.amount')),
355
353
  hasValueLabel: true,
354
+ amountLabel: (0, _i18n.t)('productVariant.amountLabel'),
356
355
  onChange: _this2.handleChange(variant),
357
356
  onClick: _this2.handleBulkNameForm(variant),
358
357
  error: hasError(variant.identifier, error),
@@ -37,7 +37,6 @@ var styles = function styles(theme) {
37
37
  iconHover: {
38
38
  margin: theme.spacing(1),
39
39
  textAlign: 'center',
40
- verticalAlign: 'middle',
41
40
  lineHeight: '24px',
42
41
  width: '24px',
43
42
  height: '24px',
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@configuratorware/configurator-frontendgui",
3
- "version": "1.57.15",
3
+ "version": "1.58.0",
4
4
  "license": "UNLICENSED",
5
5
  "private": false,
6
6
  "main": "./index.js",
7
7
  "dependencies": {
8
8
  "@babel/polyfill": "^7.12.1",
9
- "@configuratorware/scripts": "1.57.15",
9
+ "@configuratorware/scripts": "1.58.0",
10
10
  "@material-ui/core": "^4.12.4",
11
11
  "@material-ui/icons": "^4.11.3",
12
12
  "@material-ui/lab": "^4.0.0-alpha.61",
@@ -36,8 +36,8 @@
36
36
  "react-router-dom": "^5.3.4",
37
37
  "react-swipeable": "^5.5.1",
38
38
  "react-zoom-pan-pinch": "^2.1.3",
39
- "redhotmagma-graphics-editor": "1.57.15",
40
- "redhotmagma-visualization": "1.57.15",
39
+ "redhotmagma-graphics-editor": "1.58.0",
40
+ "redhotmagma-visualization": "1.58.0",
41
41
  "redux": "^4.1.0",
42
42
  "redux-logger": "^3.0.6",
43
43
  "redux-persist": "^5.10.0",
@@ -30,6 +30,7 @@
30
30
  "configurationOverview": "Meine Konfiguration",
31
31
  "productVariants": "Produktvarianten",
32
32
  "contactData": "Kontaktdaten",
33
+ "piece": "Stück",
33
34
  "pieces": "Stück",
34
35
  "vatNotice": "inkl.MwSt. zzgl. Versand",
35
36
  "vatNoticeNet": "exkl.MwSt. zzgl. Versand",
@@ -43,6 +44,7 @@
43
44
  },
44
45
  "amountSelection": {
45
46
  "total": "Gesamt: ",
47
+ "piece": "Stück",
46
48
  "amount": "Stück"
47
49
  },
48
50
  "textControlbox": {
@@ -33,6 +33,7 @@
33
33
  "notice": "Notices",
34
34
  "intoCart": "Add to basket",
35
35
  "amount": "amount",
36
+ "piece": "piece",
36
37
  "pieces": "pieces",
37
38
  "vatNotice": "VAT included plus shipping",
38
39
  "vatNoticeNet": "VAT excluded plus shipping",
@@ -43,6 +44,7 @@
43
44
  },
44
45
  "amountSelection": {
45
46
  "total": "Total: %{value} amount",
47
+ "piece": "piece",
46
48
  "amount": "pieces"
47
49
  },
48
50
  "textControlbox": {
@@ -21,6 +21,7 @@ import ObjectTools from '../ObjectTools';
21
21
  import AnalyticsWrapper from 'App/Shared/Components/Analytics/AnalyticsWrapper';
22
22
  import CustomDialog from 'Shared/Components/CustomDialog';
23
23
  import customClassName from 'App/Utils/customClassName';
24
+ import MainButton from 'Shared/Components/MainButton';
24
25
 
25
26
  const styles = theme => ({
26
27
  card: {
@@ -49,11 +50,15 @@ const styles = theme => ({
49
50
  },
50
51
  },
51
52
  },
53
+ printQualityContainer: {
54
+ flexWrap: 'nowrap',
55
+ marginBottom: 15,
56
+ gap: 8,
57
+ },
52
58
  printQuality: {
53
59
  display: 'inline-flex',
54
60
  alignItems: 'center',
55
61
  backgroundColor: '#000000',
56
- marginBottom: 15,
57
62
  borderRadius: '4px',
58
63
  color: '#fff',
59
64
  padding: '10px',
@@ -175,7 +180,7 @@ class ImageControlbox extends React.Component {
175
180
  <CardContent className={boxed ? '' : classes.CardContentWrapper}>
176
181
  <Grid container>
177
182
  <Grid item xs={12} lg={12}>
178
- <Grid container alignItems="center">
183
+ <Grid container alignItems="center" className={classes.printQualityContainer}>
179
184
  <Grid
180
185
  item
181
186
  lg={printQualityInfoExists ? 10 : 12}
@@ -236,6 +241,7 @@ class ImageControlbox extends React.Component {
236
241
  </Card>
237
242
  <CustomDialog
238
243
  open={printQualityInfoDialogOpen}
244
+ onCancel={this.handlePrintQualityInfoDialogClose}
239
245
  onClose={this.handlePrintQualityInfoDialogClose}
240
246
  >
241
247
  <DialogContent>
@@ -244,9 +250,9 @@ class ImageControlbox extends React.Component {
244
250
  </DialogContentText>
245
251
  </DialogContent>
246
252
  <DialogActions>
247
- <Button onClick={this.handlePrintQualityInfoDialogClose} color="primary">
253
+ <MainButton variant="contained" onClick={this.handlePrintQualityInfoDialogClose}>
248
254
  OK
249
- </Button>
255
+ </MainButton>
250
256
  </DialogActions>
251
257
  </CustomDialog>
252
258
  </React.Fragment>
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import clsx from 'clsx';
4
4
  import { connect } from 'react-redux';
@@ -20,7 +20,7 @@ import { editImage, WN_UPLOAD_IMAGE } from '../../Reducers/ImageGallery/Actions'
20
20
  import { createBackgroundWorkStateSelector } from '../../Reducers/UI/Selectors';
21
21
  import ImageUpload from '../../Modules/Designer/Components/ImageEditDialog/ImageUpload';
22
22
  import ColorPicker from '../../Modules/Designer/Components/ImageColorPicker/ImageColorPicker';
23
- import { getRasterURL } from '../../Modules/Designer/Utils/Transformers';
23
+ import { applySvgContentOperations, getSvgContent } from '../../Services/DesignDataService';
24
24
  import Progress from '../../Shared/Components/Progress/Progress';
25
25
  import DefaultThemeProvider from '../Configurator/ThemeProvider';
26
26
  import { createDesignerProductPreviewManager } from './DesignerProductPreviewManager';
@@ -137,8 +137,35 @@ const DesignerProductPreviewScreen = props => {
137
137
  setApplied(false);
138
138
  }, [editParams]);
139
139
 
140
- const previewSrc = useMemo(() => {
141
- return imageData && `${getRasterURL(getPreviewUrlFromImageData(imageData.preview))}?${Math.random()}`;
140
+ const [previewSrc, setPreviewSrc] = useState(null);
141
+
142
+ useEffect(() => {
143
+ if (!imageData?.preview?.url) {
144
+ setPreviewSrc(null);
145
+ return;
146
+ }
147
+
148
+ let cancelled = false;
149
+ const previewUrl = getPreviewUrlFromImageData(imageData.preview);
150
+ const isSvg = /\.svg$/.test(previewUrl);
151
+
152
+ (async () => {
153
+ if (isSvg) {
154
+ const svgContent = await getSvgContent(previewUrl, true, true);
155
+ if (cancelled) {
156
+ return;
157
+ }
158
+ setPreviewSrc(
159
+ applySvgContentOperations(imageData.original, imageData.preview, null, svgContent)
160
+ );
161
+ } else {
162
+ setPreviewSrc(`${previewUrl}?${Math.random()}`);
163
+ }
164
+ })();
165
+
166
+ return () => {
167
+ cancelled = true;
168
+ };
142
169
  }, [imageData]);
143
170
 
144
171
  const nextAction = async () => {
@@ -851,7 +851,8 @@ class AmountPrice extends React.Component {
851
851
  sizeSmall: classes.smallButtonSize,
852
852
  }}
853
853
  >
854
- {amount} {t('amountPrice.pieces')}
854
+ {amount}{' '}
855
+ {amount === 1 ? t('amountPrice.piece') : t('amountPrice.pieces')}
855
856
  </Button>
856
857
  </Grid>
857
858
  )}
@@ -113,6 +113,22 @@ it('renders without callToAction', () => {
113
113
  expect(tree).toMatchSnapshot();
114
114
  });
115
115
 
116
+ it('renders singular piece label when amount is 1', () => {
117
+ const tree = render(
118
+ <AmountPrice
119
+ width="lg"
120
+ amount={1}
121
+ priceFormatted="500,00 €"
122
+ vatNotice="VAT included plus shipping"
123
+ priceList={mockedPriceData}
124
+ onChangeAmount={noop}
125
+ ConfigurationModeSwitchComponent={getDefaultComponent}
126
+ />
127
+ );
128
+ expect(tree.text()).toContain('1 amountPrice.piece');
129
+ expect(tree.text()).not.toContain('amountPrice.pieces');
130
+ });
131
+
116
132
  it('renders receiveOfferForm', () => {
117
133
  const tree = render(
118
134
  <AmountPrice
@@ -122,7 +122,7 @@ class PriceList extends React.Component {
122
122
  gutterBottom: classes.gutterBottom,
123
123
  }}
124
124
  >
125
- {amount} {t('amountPrice.pieces')}
125
+ {amount} {amount === 1 ? t('amountPrice.piece') : t('amountPrice.pieces')}
126
126
  </Typography>
127
127
 
128
128
  <ListItemText className={classes.listItemPrimary} primary={productTitle} />
@@ -69,6 +69,14 @@ it('does not show price for optional cost unchecked', async () => {
69
69
  expect(TypoWrapper).toHaveText('');
70
70
  });
71
71
 
72
+ it('renders singular piece label when amount is 1 and list is empty', () => {
73
+ const tree = render(
74
+ <PriceList amount={1} list={[]} productTitle="Demo Hoodie" onSelectableChanged={noop} />
75
+ );
76
+ expect(tree.text()).toContain('1 amountPrice.piece');
77
+ expect(tree.text()).not.toContain('amountPrice.pieces');
78
+ });
79
+
72
80
  it('shows the price for optional cost selected', () => {
73
81
  const priceList = [
74
82
  {
@@ -176,10 +176,9 @@ class ProductVariant extends React.Component {
176
176
  this.setState(state => ({
177
177
  sizes: { ...state.sizes, [size.identifier]: value },
178
178
  }));
179
- } else {
180
- if (isFunction(this.props.handleChange)) {
181
- this.props.handleChange(variant, size, value);
182
- }
179
+ }
180
+ if (isFunction(this.props.handleChange)) {
181
+ this.props.handleChange(variant, size, value);
183
182
  }
184
183
  };
185
184
 
@@ -373,9 +372,15 @@ class ProductVariant extends React.Component {
373
372
  )}
374
373
  {get(variant, 'sizes.length', 0) === 0 && (
375
374
  <AmountInput
376
- value={variant.amount + ' ' + t('amountSelection.amount')}
377
- amountLabel={t('productVariant.amountLabel')}
375
+ value={
376
+ variant.amount +
377
+ ' ' +
378
+ (variant.amount === 1
379
+ ? t('amountSelection.piece')
380
+ : t('amountSelection.amount'))
381
+ }
378
382
  hasValueLabel={true}
383
+ amountLabel={t('productVariant.amountLabel')}
379
384
  onChange={this.handleChange(variant)}
380
385
  onClick={this.handleBulkNameForm(variant)}
381
386
  error={hasError(variant.identifier, error)}
@@ -16,7 +16,6 @@ const styles = theme => ({
16
16
  iconHover: {
17
17
  margin: theme.spacing(1),
18
18
  textAlign: 'center',
19
- verticalAlign: 'middle',
20
19
  lineHeight: '24px',
21
20
  width: '24px',
22
21
  height: '24px',