@opengeoweb/taf 2.1.1 → 2.1.2

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.
@@ -1 +1 @@
1
- export declare const useStyles: (props?: any) => import("@material-ui/styles").ClassNameMap<"textFieldGridItem" | "textFieldInput" | "textFieldLabel" | "textFieldLabelShrink" | "formRow" | "tacTitle" | "tacText" | "tacLine" | "addButton" | "lifecycleButtons" | "actionButton" | "lastButton" | "backdrop">;
1
+ export declare const useStyles: (props?: any) => import("@material-ui/styles").ClassNameMap<"textFieldGridItem" | "textFieldInput" | "textFieldLabel" | "textFieldLabelShrink" | "formRow" | "tacTitle" | "tacText" | "tacLine" | "addButton" | "lifecycleButtons" | "actionButton" | "lastButton" | "backdrop" | "emptyButtonContainer">;
@@ -7,6 +7,7 @@ export interface TafFormRowProps extends GridProps {
7
7
  index?: number;
8
8
  lastRow?: boolean;
9
9
  onRemoveChangeGroup?: (index: number) => void;
10
+ onAddChangeGroup?: (index: number) => void;
10
11
  field?: Partial<ArrayField>;
11
12
  dragHandle?: React.ReactElement;
12
13
  }
@@ -8,6 +8,9 @@ export declare const invalidWindDirectionMinMaxMessage = "Invalid wind direction
8
8
  export declare const invalidWindDirectionStepMessage = "Invalid wind direction, the direction must be rounded to the nearest step of 10 degrees";
9
9
  export declare const invalidWindSpeedMessage = "Invalid wind speed, expected a speed equal to 2 digits or P99";
10
10
  export declare const invalidWindGustsMessage = "Invalid wind gusts, expected gusts equal to 2 digits or P99";
11
- export declare const invalidWindGustSpeedCombinationMessage = "Invalid wind, wind gusts must at least be 10 knots greater than the wind speed";
11
+ export declare const invalidWindGustSpeedCombinationMessage = "Invalid wind gusts, gusts must at least be 10 knots greater than the wind speed";
12
+ export declare const invalidWindGustSpeedBelow15Message = "Invalid wind gusts, gusts are only allowed for a minimum wind speed of 15 knots";
12
13
  export declare const invalidGustForWindP99Message = "Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99";
14
+ export declare const invalidWindSpeedForDirectionZeroMessage = "Invalid wind speed, if direction is 000, speed can only be 00";
15
+ export declare const invalidWindSpeedForDirectionNonZeroMessage = "Invalid wind speed, if direction is not 000, speed can not be 00";
13
16
  export declare const validateWindField: (value: string) => boolean | string;
@@ -3,7 +3,19 @@ declare const _default: {
3
3
  title: string;
4
4
  };
5
5
  export default _default;
6
- export declare const NewTafForm: () => React.ReactElement;
7
- export declare const EditTafForm: () => React.ReactElement;
8
- export declare const ViewTafForm: () => React.ReactElement;
9
- export declare const EditTafFormWithMultipleSameStartDates: () => React.ReactElement;
6
+ export declare const NewTafForm: {
7
+ (): React.ReactElement;
8
+ storyName: string;
9
+ };
10
+ export declare const EditTafForm: {
11
+ (): React.ReactElement;
12
+ storyName: string;
13
+ };
14
+ export declare const ViewTafForm: {
15
+ (): React.ReactElement;
16
+ storyName: string;
17
+ };
18
+ export declare const EditTafFormWithMultipleSameStartDates: {
19
+ (): React.ReactElement;
20
+ storyName: string;
21
+ };
@@ -25,6 +25,6 @@ export declare const useDragOrder: (trigger: (name?: string | string[]) => Promi
25
25
  activeDraggingIndex: number;
26
26
  };
27
27
  export declare const getConfirmationDialogTitle: (action: TafActions) => string;
28
- export declare const getConfirmationDialogContent: (action: TafActions) => string;
28
+ export declare const getConfirmationDialogContent: (action: TafActions, isFormDirty?: boolean) => string;
29
29
  export declare const getConfirmationDialogButtonLabel: (action: TafActions) => string;
30
30
  export {};
@@ -4,3 +4,15 @@ declare const _default: {
4
4
  };
5
5
  export default _default;
6
6
  export declare const TafListRefreshDemo: React.FC;
7
+ export declare const ListUpcoming: {
8
+ (): React.ReactElement;
9
+ storyName: string;
10
+ };
11
+ export declare const ListCurrent: {
12
+ (): React.ReactElement;
13
+ storyName: string;
14
+ };
15
+ export declare const ListExpired: {
16
+ (): React.ReactElement;
17
+ storyName: string;
18
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -2,9 +2,16 @@ import { TafFromBackend } from '../../types';
2
2
  export declare const dateFormatUTC = "YYYY-MM-DDTHH:mmZ";
3
3
  export declare const fakeTafList: TafFromBackend[];
4
4
  export declare const fakeNewTaf: TafFromBackend;
5
+ export declare const fakeNewFixedTaf: TafFromBackend;
5
6
  export declare const fakeExpiredTaf: TafFromBackend;
6
7
  export declare const fakePublishedTaf: TafFromBackend;
8
+ export declare const fakePublishedFixedTaf: TafFromBackend;
7
9
  export declare const fakeDraftTaf: TafFromBackend;
10
+ export declare const fakeDraftFixedTaf: TafFromBackend;
8
11
  export declare const fakeDraftAmendmentTaf: TafFromBackend;
12
+ export declare const fakeDraftAmendmentFixedTaf: TafFromBackend;
9
13
  export declare const fakeAmendmentTaf: TafFromBackend;
14
+ export declare const fakeAmendmentFixedTaf: TafFromBackend;
15
+ export declare const fakePublishedTafWithChangeGroups: TafFromBackend;
10
16
  export declare const fakePublishedTafWithSameDates: TafFromBackend;
17
+ export declare const fakePublishedTafWithSameDatesFixed: TafFromBackend;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/taf",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "GeoWeb TAF library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -14,22 +14,22 @@
14
14
  "peerDependencies": {
15
15
  "react": "16.14.0",
16
16
  "@material-ui/core": "^4.11.0",
17
- "@opengeoweb/theme": "2.1.1",
17
+ "@opengeoweb/theme": "2.1.2",
18
18
  "@material-ui/lab": "^4.0.0-alpha.56",
19
19
  "@material-ui/icons": "^4.9.1",
20
20
  "react-hook-form": "^6.12.1",
21
- "@opengeoweb/shared": "2.1.1",
21
+ "@opengeoweb/shared": "2.1.2",
22
22
  "react-draggable": "^4.4.3",
23
23
  "re-resizable": "^6.9.0",
24
24
  "lodash": "^4.17.20",
25
- "@opengeoweb/form-fields": "2.1.1",
26
- "@material-ui/pickers": "^3.2.10",
27
- "@date-io/moment": "^1.3.13",
25
+ "@opengeoweb/form-fields": "2.1.2",
28
26
  "moment": "^2.29.0",
27
+ "@material-ui/pickers": "^3.2.10",
29
28
  "moment-timezone": "^0.5.31",
29
+ "@date-io/moment": "^1.3.13",
30
30
  "axios": "^0.21.4",
31
31
  "react-sortablejs": "^6.0.0",
32
- "@opengeoweb/api": "2.1.1",
32
+ "@opengeoweb/api": "2.1.2",
33
33
  "sortablejs": "^1.14.0"
34
34
  }
35
35
  }
package/taf.esm.js CHANGED
@@ -3,8 +3,8 @@ import React__default from 'react';
3
3
  import { makeStyles, Grid, TextField, IconButton, Tooltip, Chip, SvgIcon, Typography, Button, Dialog, DialogTitle, DialogContent, Card, Accordion, AccordionSummary, AccordionDetails, Backdrop, CircularProgress } from '@material-ui/core';
4
4
  import { Skeleton } from '@material-ui/lab';
5
5
  import moment from 'moment';
6
- import { useIsMounted, useDraftFormHelpers, ToolContainerDraggable, useConfirmationDialog, usePreventBrowserClose, usePrevious, StatusTag, AlertBanner, ConfirmationServiceProvider, usePoller, isAxiosError, getAxiosErrorMessage } from '@opengeoweb/shared';
7
- import { Delete, DragIndicator, Add, Close, InfoOutlined, Create, CheckCircle, Schedule, OpenInNew, ExpandMore } from '@material-ui/icons';
6
+ import { useIsMounted, useDraftFormHelpers, ToolContainerDraggable, useConfirmationDialog, usePreventBrowserClose, StatusTag, AlertBanner, ConfirmationServiceProvider, usePoller, isAxiosError, getAxiosErrorMessage } from '@opengeoweb/shared';
7
+ import { Add, Delete, DragIndicator, Close, InfoOutlined, Create, CheckCircle, Schedule, OpenInNew, ExpandMore } from '@material-ui/icons';
8
8
  import { useApiContext, useApi, createApiInstance } from '@opengeoweb/api';
9
9
  import { getDeepProperty, ReactHookFormHiddenInput, ReactHookFormProvider } from '@opengeoweb/form-fields';
10
10
  import { useFormContext, Controller, useFieldArray } from 'react-hook-form';
@@ -1446,8 +1446,11 @@ var invalidWindDirectionMinMaxMessage = 'Invalid wind direction, the direction m
1446
1446
  var invalidWindDirectionStepMessage = 'Invalid wind direction, the direction must be rounded to the nearest step of 10 degrees';
1447
1447
  var invalidWindSpeedMessage = 'Invalid wind speed, expected a speed equal to 2 digits or P99';
1448
1448
  var invalidWindGustsMessage = 'Invalid wind gusts, expected gusts equal to 2 digits or P99';
1449
- var invalidWindGustSpeedCombinationMessage = 'Invalid wind, wind gusts must at least be 10 knots greater than the wind speed';
1450
- var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99'; // validations
1449
+ var invalidWindGustSpeedCombinationMessage = 'Invalid wind gusts, gusts must at least be 10 knots greater than the wind speed';
1450
+ var invalidWindGustSpeedBelow15Message = 'Invalid wind gusts, gusts are only allowed for a minimum wind speed of 15 knots';
1451
+ var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99';
1452
+ var invalidWindSpeedForDirectionZeroMessage = 'Invalid wind speed, if direction is 000, speed can only be 00';
1453
+ var invalidWindSpeedForDirectionNonZeroMessage = 'Invalid wind speed, if direction is not 000, speed can not be 00'; // validations
1451
1454
 
1452
1455
  var validateDirection = function validateDirection(value) {
1453
1456
  // if not a number
@@ -1495,8 +1498,16 @@ var validateUnit = function validateUnit(value) {
1495
1498
  return value === 'KT' || value === '' || invalidWindMessage;
1496
1499
  };
1497
1500
 
1498
- var validateWindInterSection = function validateWindInterSection(speed, gust) {
1499
- if (gust === '' || gust === 'P99') {
1501
+ var validateWindInterSection = function validateWindInterSection(speed, gust, direction) {
1502
+ if (direction === '000' && speed !== '00') {
1503
+ return invalidWindSpeedForDirectionZeroMessage;
1504
+ }
1505
+
1506
+ if (direction !== '000' && speed === '00') {
1507
+ return invalidWindSpeedForDirectionNonZeroMessage;
1508
+ }
1509
+
1510
+ if (gust === '') {
1500
1511
  return true;
1501
1512
  }
1502
1513
 
@@ -1504,7 +1515,15 @@ var validateWindInterSection = function validateWindInterSection(speed, gust) {
1504
1515
  return invalidGustForWindP99Message;
1505
1516
  }
1506
1517
 
1507
- return parseInt(gust, 10) - parseInt(speed, 10) >= 10 || invalidWindGustSpeedCombinationMessage;
1518
+ if (parseInt(speed, 10) < 15) {
1519
+ return invalidWindGustSpeedBelow15Message;
1520
+ }
1521
+
1522
+ if (gust !== 'P99' && parseInt(gust, 10) - parseInt(speed, 10) < 10) {
1523
+ return invalidWindGustSpeedCombinationMessage;
1524
+ }
1525
+
1526
+ return true;
1508
1527
  }; // Main validation function
1509
1528
 
1510
1529
 
@@ -1530,7 +1549,7 @@ var validateWindField = function validateWindField(value) {
1530
1549
  } // Then do any intersection validation
1531
1550
 
1532
1551
 
1533
- return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust);
1552
+ return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust, windValidationIndv.direction);
1534
1553
  };
1535
1554
 
1536
1555
  var formatSingleCloud = function formatSingleCloud(value) {
@@ -2399,11 +2418,22 @@ var getConfirmationDialogTitle = function getConfirmationDialogTitle(action) {
2399
2418
  return 'Cancel TAF';
2400
2419
  };
2401
2420
  var getConfirmationDialogContent = function getConfirmationDialogContent(action) {
2402
- if (action === 'DISCARD') return 'Are you sure you would like to discard this TAF? Any changes made will be lost';
2403
- if (action === 'PUBLISH') return 'Are you sure you want to publish this TAF?';
2404
- if (action === 'AMEND') return 'Do you want to publish it?';
2405
- if (action === 'CORRECT') return 'Do you want to publish it?';
2406
- return 'Are you sure you want to cancel this TAF?';
2421
+ var isFormDirty = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
2422
+
2423
+ switch (action) {
2424
+ case 'DISCARD':
2425
+ return 'Are you sure you would like to discard this TAF? Any changes made will be lost';
2426
+
2427
+ case 'PUBLISH':
2428
+ return 'Are you sure you want to publish this TAF?';
2429
+
2430
+ case 'AMEND':
2431
+ case 'CORRECT':
2432
+ return isFormDirty ? 'Do you want to publish it?' : 'Are you sure you want to publish the TAF? You did not make any changes!';
2433
+
2434
+ default:
2435
+ return 'Are you sure you want to cancel this TAF?';
2436
+ }
2407
2437
  };
2408
2438
  var getConfirmationDialogButtonLabel = function getConfirmationDialogButtonLabel(action) {
2409
2439
  switch (action) {
@@ -2480,7 +2510,7 @@ var useStyles$9 = makeStyles(function (theme) {
2480
2510
  borderRadius: '3px'
2481
2511
  },
2482
2512
  addButton: {
2483
- marginRight: '38px'
2513
+ marginRight: '48px'
2484
2514
  },
2485
2515
  lifecycleButtons: {
2486
2516
  marginTop: '40px',
@@ -2495,6 +2525,9 @@ var useStyles$9 = makeStyles(function (theme) {
2495
2525
  backdrop: {
2496
2526
  zIndex: 1500,
2497
2527
  color: '#fff'
2528
+ },
2529
+ emptyButtonContainer: {
2530
+ minWidth: 96
2498
2531
  }
2499
2532
  };
2500
2533
  });
@@ -2739,8 +2772,10 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2739
2772
  field = _a$field === void 0 ? null : _a$field,
2740
2773
  _a$dragHandle = _a.dragHandle,
2741
2774
  dragHandle = _a$dragHandle === void 0 ? null : _a$dragHandle,
2742
- props = __rest(_a, ["isChangeGroup", "disabled", "index", "lastRow", "onRemoveChangeGroup", "field", "dragHandle"]);
2775
+ onAddChangeGroup = _a.onAddChangeGroup,
2776
+ props = __rest(_a, ["isChangeGroup", "disabled", "index", "lastRow", "onRemoveChangeGroup", "field", "dragHandle", "onAddChangeGroup"]);
2743
2777
 
2778
+ var classes = useStyles$9();
2744
2779
  var namePrefix = !isChangeGroup ? 'baseForecast' : "changeGroups[".concat(index, "]");
2745
2780
  var fieldNames = getFieldNames(isChangeGroup, index);
2746
2781
 
@@ -2827,6 +2862,9 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
2827
2862
  return nextRowValidValue ? [nextValidFieldName] : [];
2828
2863
  };
2829
2864
 
2865
+ var addChangeGroup = React__default.useCallback(function () {
2866
+ onAddChangeGroup(index);
2867
+ }, [onAddChangeGroup, index]);
2830
2868
  return /*#__PURE__*/React__default.createElement(Grid, Object.assign({
2831
2869
  "data-testid": "row-".concat(namePrefix),
2832
2870
  container: true,
@@ -3081,12 +3119,20 @@ var TafFormRow = /*#__PURE__*/React__default.forwardRef(function (_a, ref) {
3081
3119
  }
3082
3120
  }
3083
3121
  }), (!isChangeGroup || disabled) && /*#__PURE__*/React__default.createElement(Grid, {
3122
+ className: classes.emptyButtonContainer,
3084
3123
  item: true,
3085
3124
  xs: true
3086
- }), isChangeGroup && !disabled && /*#__PURE__*/React__default.createElement(Grid, {
3087
- item: true,
3088
- xs: true
3089
- }, /*#__PURE__*/React__default.createElement(IconButton, Object.assign({
3125
+ }, !disabled && /*#__PURE__*/React__default.createElement(IconButton, {
3126
+ "aria-label": "add",
3127
+ "data-testid": "add-changeGroup",
3128
+ onClick: addChangeGroup
3129
+ }, /*#__PURE__*/React__default.createElement(Add, null))), isChangeGroup && !disabled && /*#__PURE__*/React__default.createElement(Grid, {
3130
+ item: true
3131
+ }, /*#__PURE__*/React__default.createElement(IconButton, {
3132
+ "aria-label": "add",
3133
+ "data-testid": "add-changeGroup",
3134
+ onClick: addChangeGroup
3135
+ }, /*#__PURE__*/React__default.createElement(Add, null)), /*#__PURE__*/React__default.createElement(IconButton, Object.assign({
3090
3136
  "aria-label": "delete",
3091
3137
  "data-testid": "remove-changeGroup",
3092
3138
  onClick: function onClick() {
@@ -3640,7 +3686,7 @@ var TafForm = function TafForm(_ref) {
3640
3686
  var showConfirmDialog = function showConfirmDialog(action) {
3641
3687
  return confirmDialog({
3642
3688
  title: getConfirmationDialogTitle(action),
3643
- description: getConfirmationDialogContent(action),
3689
+ description: getConfirmationDialogContent(action, isFormDirty),
3644
3690
  confirmLabel: getConfirmationDialogButtonLabel(action)
3645
3691
  });
3646
3692
  };
@@ -3689,32 +3735,58 @@ var TafForm = function TafForm(_ref) {
3689
3735
  name: 'changeGroups'
3690
3736
  }),
3691
3737
  fields = _useFieldArray.fields,
3692
- append = _useFieldArray.append,
3738
+ insert = _useFieldArray.insert,
3693
3739
  remove = _useFieldArray.remove,
3694
3740
  move = _useFieldArray.move;
3695
3741
 
3696
- var lastTotalFields = usePrevious(fields.length);
3697
-
3698
3742
  var _useDragOrder = useDragOrder(trigger, move, onFormBlur),
3699
3743
  onChangeOrder = _useDragOrder.onChangeOrder,
3700
3744
  onStartDrag = _useDragOrder.onStartDrag,
3701
3745
  activeDraggingIndex = _useDragOrder.activeDraggingIndex;
3702
3746
 
3703
3747
  var onRemoverow = function onRemoverow(index) {
3704
- remove(index);
3748
+ confirmDialog({
3749
+ title: 'Delete row',
3750
+ description: 'Are you sure you want to delete this row?',
3751
+ confirmLabel: 'Yes'
3752
+ }).then(function () {
3753
+ remove(index);
3754
+ trigger(); // trigger all fields validation
3755
+
3756
+ onFormBlur();
3757
+ });
3758
+ }; // TODO: https://gitlab.com/opengeoweb/opengeoweb/-/issues/726 insert (react-hook-form) does not work well with focus (only last row will get focus), so added this dirty solution. This can be fixed in a better way in react-hook-form v7
3759
+
3760
+
3761
+ var focusFirstInput = function focusFirstInput(rowIndex) {
3762
+ var nextRowFirstInput = document.querySelector("[name=\"changeGroups[".concat(rowIndex, "].probability\"]"));
3763
+
3764
+ if (nextRowFirstInput) {
3765
+ nextRowFirstInput.focus();
3766
+ }
3705
3767
  };
3706
3768
 
3707
3769
  var onAddRow = function onAddRow() {
3708
- append(emptyChangegroup, true);
3770
+ var rowIndex = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1;
3771
+ var newRowIndex = rowIndex + 1;
3772
+ insert(newRowIndex, emptyChangegroup, false); // adding rows and validation don't work well combined, with always skipping validation of fields of rows above the one the inserted row. A setTimeout prevents this issue
3773
+
3774
+ setTimeout(function () {
3775
+ focusFirstInput(newRowIndex);
3776
+ trigger(); // trigger all fields validation
3777
+ }, 0);
3709
3778
  };
3710
3779
 
3711
3780
  React__default.useEffect(function () {
3712
- if (isFormDirty && fields.length !== lastTotalFields) {
3713
- onFormBlur();
3714
- trigger(); // trigger all field validation,
3781
+ if (!isDisabled) {
3782
+ // trigger validation on all changegroup validity times after initial load since they could be outside the new validity period
3783
+ var validFields = fields.map(function (_field, index) {
3784
+ return "changeGroups[".concat(index, "].valid");
3785
+ });
3786
+ trigger(validFields);
3715
3787
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3716
3788
 
3717
- }, [fields.length, isFormDirty]);
3789
+ }, []);
3718
3790
  return /*#__PURE__*/React__default.createElement("form", {
3719
3791
  onSubmit: handleSubmit(onSubmit),
3720
3792
  onBlur: onFormBlur
@@ -3730,7 +3802,8 @@ var TafForm = function TafForm(_ref) {
3730
3802
  "data-testid": "tafform-tac-message-line"
3731
3803
  }, line);
3732
3804
  })), /*#__PURE__*/React__default.createElement(TafFormRow, {
3733
- disabled: isDisabled
3805
+ disabled: isDisabled,
3806
+ onAddChangeGroup: onAddRow
3734
3807
  }), /*#__PURE__*/React__default.createElement(ReactSortable, {
3735
3808
  tag: "div",
3736
3809
  list: fields,
@@ -3754,6 +3827,7 @@ var TafForm = function TafForm(_ref) {
3754
3827
  lastRow: fields.length === index + 1,
3755
3828
  disabled: isDisabled,
3756
3829
  onRemoveChangeGroup: onRemoverow,
3830
+ onAddChangeGroup: onAddRow,
3757
3831
  isChangeGroup: true,
3758
3832
  dragHandle: /*#__PURE__*/React__default.createElement(DragHandle, {
3759
3833
  isDisabled: fields.length === 1 || isDisabled,
@@ -3761,20 +3835,10 @@ var TafForm = function TafForm(_ref) {
3761
3835
  index: index
3762
3836
  })
3763
3837
  });
3764
- })), !isDisabled && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement(Grid, {
3765
- container: true,
3766
- item: true,
3767
- xs: 12,
3768
- justify: "flex-end"
3769
- }, /*#__PURE__*/React__default.createElement(IconButton, {
3770
- "aria-label": "add",
3771
- "data-testid": "add-changeGroup",
3772
- onClick: onAddRow,
3773
- className: classes.addButton
3774
- }, /*#__PURE__*/React__default.createElement(Add, null))), /*#__PURE__*/React__default.createElement(TafFormButtons, {
3838
+ })), !isDisabled && /*#__PURE__*/React__default.createElement(TafFormButtons, {
3775
3839
  tafAction: tafAction,
3776
3840
  onTafFormButtonPress: onTafButtonPress
3777
- })), hiddenInputFields.map(function (field) {
3841
+ }), hiddenInputFields.map(function (field) {
3778
3842
  return /*#__PURE__*/React__default.createElement(ReactHookFormHiddenInput, {
3779
3843
  name: field,
3780
3844
  key: field
package/taf.umd.js CHANGED
@@ -1395,8 +1395,11 @@
1395
1395
  var invalidWindDirectionStepMessage = 'Invalid wind direction, the direction must be rounded to the nearest step of 10 degrees';
1396
1396
  var invalidWindSpeedMessage = 'Invalid wind speed, expected a speed equal to 2 digits or P99';
1397
1397
  var invalidWindGustsMessage = 'Invalid wind gusts, expected gusts equal to 2 digits or P99';
1398
- var invalidWindGustSpeedCombinationMessage = 'Invalid wind, wind gusts must at least be 10 knots greater than the wind speed';
1399
- var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99'; // validations
1398
+ var invalidWindGustSpeedCombinationMessage = 'Invalid wind gusts, gusts must at least be 10 knots greater than the wind speed';
1399
+ var invalidWindGustSpeedBelow15Message = 'Invalid wind gusts, gusts are only allowed for a minimum wind speed of 15 knots';
1400
+ var invalidGustForWindP99Message = 'Invalid wind speed and gust combination, if wind is P99, gusts can only be empty or P99';
1401
+ var invalidWindSpeedForDirectionZeroMessage = 'Invalid wind speed, if direction is 000, speed can only be 00';
1402
+ var invalidWindSpeedForDirectionNonZeroMessage = 'Invalid wind speed, if direction is not 000, speed can not be 00'; // validations
1400
1403
 
1401
1404
  var validateDirection = function validateDirection(value) {
1402
1405
  // if not a number
@@ -1444,8 +1447,16 @@
1444
1447
  return value === 'KT' || value === '' || invalidWindMessage;
1445
1448
  };
1446
1449
 
1447
- var validateWindInterSection = function validateWindInterSection(speed, gust) {
1448
- if (gust === '' || gust === 'P99') {
1450
+ var validateWindInterSection = function validateWindInterSection(speed, gust, direction) {
1451
+ if (direction === '000' && speed !== '00') {
1452
+ return invalidWindSpeedForDirectionZeroMessage;
1453
+ }
1454
+
1455
+ if (direction !== '000' && speed === '00') {
1456
+ return invalidWindSpeedForDirectionNonZeroMessage;
1457
+ }
1458
+
1459
+ if (gust === '') {
1449
1460
  return true;
1450
1461
  }
1451
1462
 
@@ -1453,7 +1464,15 @@
1453
1464
  return invalidGustForWindP99Message;
1454
1465
  }
1455
1466
 
1456
- return parseInt(gust, 10) - parseInt(speed, 10) >= 10 || invalidWindGustSpeedCombinationMessage;
1467
+ if (parseInt(speed, 10) < 15) {
1468
+ return invalidWindGustSpeedBelow15Message;
1469
+ }
1470
+
1471
+ if (gust !== 'P99' && parseInt(gust, 10) - parseInt(speed, 10) < 10) {
1472
+ return invalidWindGustSpeedCombinationMessage;
1473
+ }
1474
+
1475
+ return true;
1457
1476
  }; // Main validation function
1458
1477
 
1459
1478
 
@@ -1479,7 +1498,7 @@
1479
1498
  } // Then do any intersection validation
1480
1499
 
1481
1500
 
1482
- return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust);
1501
+ return validateWindInterSection(windValidationIndv.speed, windValidationIndv.gust, windValidationIndv.direction);
1483
1502
  };
1484
1503
 
1485
1504
  var formatSingleCloud = function formatSingleCloud(value) {
@@ -2351,12 +2370,25 @@
2351
2370
  if (action === 'CORRECT') return 'This TAF is a correction';
2352
2371
  return 'Cancel TAF';
2353
2372
  };
2354
- var getConfirmationDialogContent = function getConfirmationDialogContent(action) {
2355
- if (action === 'DISCARD') return 'Are you sure you would like to discard this TAF? Any changes made will be lost';
2356
- if (action === 'PUBLISH') return 'Are you sure you want to publish this TAF?';
2357
- if (action === 'AMEND') return 'Do you want to publish it?';
2358
- if (action === 'CORRECT') return 'Do you want to publish it?';
2359
- return 'Are you sure you want to cancel this TAF?';
2373
+ var getConfirmationDialogContent = function getConfirmationDialogContent(action, isFormDirty) {
2374
+ if (isFormDirty === void 0) {
2375
+ isFormDirty = true;
2376
+ }
2377
+
2378
+ switch (action) {
2379
+ case 'DISCARD':
2380
+ return 'Are you sure you would like to discard this TAF? Any changes made will be lost';
2381
+
2382
+ case 'PUBLISH':
2383
+ return 'Are you sure you want to publish this TAF?';
2384
+
2385
+ case 'AMEND':
2386
+ case 'CORRECT':
2387
+ return isFormDirty ? 'Do you want to publish it?' : 'Are you sure you want to publish the TAF? You did not make any changes!';
2388
+
2389
+ default:
2390
+ return 'Are you sure you want to cancel this TAF?';
2391
+ }
2360
2392
  };
2361
2393
  var getConfirmationDialogButtonLabel = function getConfirmationDialogButtonLabel(action) {
2362
2394
  switch (action) {
@@ -2433,7 +2465,7 @@
2433
2465
  borderRadius: '3px'
2434
2466
  },
2435
2467
  addButton: {
2436
- marginRight: '38px'
2468
+ marginRight: '48px'
2437
2469
  },
2438
2470
  lifecycleButtons: {
2439
2471
  marginTop: '40px',
@@ -2448,6 +2480,9 @@
2448
2480
  backdrop: {
2449
2481
  zIndex: 1500,
2450
2482
  color: '#fff'
2483
+ },
2484
+ emptyButtonContainer: {
2485
+ minWidth: 96
2451
2486
  }
2452
2487
  };
2453
2488
  });
@@ -2691,8 +2726,10 @@
2691
2726
  field = _f === void 0 ? null : _f,
2692
2727
  _g = _a.dragHandle,
2693
2728
  dragHandle = _g === void 0 ? null : _g,
2694
- props = __rest(_a, ["isChangeGroup", "disabled", "index", "lastRow", "onRemoveChangeGroup", "field", "dragHandle"]);
2729
+ onAddChangeGroup = _a.onAddChangeGroup,
2730
+ props = __rest(_a, ["isChangeGroup", "disabled", "index", "lastRow", "onRemoveChangeGroup", "field", "dragHandle", "onAddChangeGroup"]);
2695
2731
 
2732
+ var classes = useStyles$9();
2696
2733
  var namePrefix = !isChangeGroup ? 'baseForecast' : "changeGroups[" + index + "]";
2697
2734
  var fieldNames = getFieldNames(isChangeGroup, index);
2698
2735
 
@@ -2779,6 +2816,9 @@
2779
2816
  return nextRowValidValue ? [nextValidFieldName] : [];
2780
2817
  };
2781
2818
 
2819
+ var addChangeGroup = React__default["default"].useCallback(function () {
2820
+ onAddChangeGroup(index);
2821
+ }, [onAddChangeGroup, index]);
2782
2822
  return /*#__PURE__*/React__default["default"].createElement(core.Grid, __assign({
2783
2823
  "data-testid": "row-" + namePrefix,
2784
2824
  container: true,
@@ -3033,12 +3073,20 @@
3033
3073
  }
3034
3074
  }
3035
3075
  }), (!isChangeGroup || disabled) && /*#__PURE__*/React__default["default"].createElement(core.Grid, {
3076
+ className: classes.emptyButtonContainer,
3036
3077
  item: true,
3037
3078
  xs: true
3038
- }), isChangeGroup && !disabled && /*#__PURE__*/React__default["default"].createElement(core.Grid, {
3039
- item: true,
3040
- xs: true
3041
- }, /*#__PURE__*/React__default["default"].createElement(core.IconButton, __assign({
3079
+ }, !disabled && /*#__PURE__*/React__default["default"].createElement(core.IconButton, {
3080
+ "aria-label": "add",
3081
+ "data-testid": "add-changeGroup",
3082
+ onClick: addChangeGroup
3083
+ }, /*#__PURE__*/React__default["default"].createElement(icons.Add, null))), isChangeGroup && !disabled && /*#__PURE__*/React__default["default"].createElement(core.Grid, {
3084
+ item: true
3085
+ }, /*#__PURE__*/React__default["default"].createElement(core.IconButton, {
3086
+ "aria-label": "add",
3087
+ "data-testid": "add-changeGroup",
3088
+ onClick: addChangeGroup
3089
+ }, /*#__PURE__*/React__default["default"].createElement(icons.Add, null)), /*#__PURE__*/React__default["default"].createElement(core.IconButton, __assign({
3042
3090
  "aria-label": "delete",
3043
3091
  "data-testid": "remove-changeGroup",
3044
3092
  onClick: function onClick() {
@@ -3547,7 +3595,7 @@
3547
3595
  var showConfirmDialog = function showConfirmDialog(action) {
3548
3596
  return confirmDialog({
3549
3597
  title: getConfirmationDialogTitle(action),
3550
- description: getConfirmationDialogContent(action),
3598
+ description: getConfirmationDialogContent(action, isFormDirty),
3551
3599
  confirmLabel: getConfirmationDialogButtonLabel(action)
3552
3600
  });
3553
3601
  };
@@ -3596,32 +3644,61 @@
3596
3644
  name: 'changeGroups'
3597
3645
  }),
3598
3646
  fields = _h.fields,
3599
- append = _h.append,
3647
+ insert = _h.insert,
3600
3648
  remove = _h.remove,
3601
3649
  move = _h.move;
3602
3650
 
3603
- var lastTotalFields = shared.usePrevious(fields.length);
3604
-
3605
3651
  var _j = useDragOrder(trigger, move, onFormBlur),
3606
3652
  onChangeOrder = _j.onChangeOrder,
3607
3653
  onStartDrag = _j.onStartDrag,
3608
3654
  activeDraggingIndex = _j.activeDraggingIndex;
3609
3655
 
3610
3656
  var onRemoverow = function onRemoverow(index) {
3611
- remove(index);
3657
+ confirmDialog({
3658
+ title: 'Delete row',
3659
+ description: 'Are you sure you want to delete this row?',
3660
+ confirmLabel: 'Yes'
3661
+ }).then(function () {
3662
+ remove(index);
3663
+ trigger(); // trigger all fields validation
3664
+
3665
+ onFormBlur();
3666
+ });
3667
+ }; // TODO: https://gitlab.com/opengeoweb/opengeoweb/-/issues/726 insert (react-hook-form) does not work well with focus (only last row will get focus), so added this dirty solution. This can be fixed in a better way in react-hook-form v7
3668
+
3669
+
3670
+ var focusFirstInput = function focusFirstInput(rowIndex) {
3671
+ var nextRowFirstInput = document.querySelector("[name=\"changeGroups[" + rowIndex + "].probability\"]");
3672
+
3673
+ if (nextRowFirstInput) {
3674
+ nextRowFirstInput.focus();
3675
+ }
3612
3676
  };
3613
3677
 
3614
- var onAddRow = function onAddRow() {
3615
- append(emptyChangegroup, true);
3678
+ var onAddRow = function onAddRow(rowIndex) {
3679
+ if (rowIndex === void 0) {
3680
+ rowIndex = -1;
3681
+ }
3682
+
3683
+ var newRowIndex = rowIndex + 1;
3684
+ insert(newRowIndex, emptyChangegroup, false); // adding rows and validation don't work well combined, with always skipping validation of fields of rows above the one the inserted row. A setTimeout prevents this issue
3685
+
3686
+ setTimeout(function () {
3687
+ focusFirstInput(newRowIndex);
3688
+ trigger(); // trigger all fields validation
3689
+ }, 0);
3616
3690
  };
3617
3691
 
3618
3692
  React__default["default"].useEffect(function () {
3619
- if (isFormDirty && fields.length !== lastTotalFields) {
3620
- onFormBlur();
3621
- trigger(); // trigger all field validation,
3693
+ if (!isDisabled) {
3694
+ // trigger validation on all changegroup validity times after initial load since they could be outside the new validity period
3695
+ var validFields = fields.map(function (_field, index) {
3696
+ return "changeGroups[" + index + "].valid";
3697
+ });
3698
+ trigger(validFields);
3622
3699
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3623
3700
 
3624
- }, [fields.length, isFormDirty]);
3701
+ }, []);
3625
3702
  return /*#__PURE__*/React__default["default"].createElement("form", {
3626
3703
  onSubmit: handleSubmit(onSubmit),
3627
3704
  onBlur: onFormBlur
@@ -3637,7 +3714,8 @@
3637
3714
  "data-testid": "tafform-tac-message-line"
3638
3715
  }, line);
3639
3716
  })), /*#__PURE__*/React__default["default"].createElement(TafFormRow, {
3640
- disabled: isDisabled
3717
+ disabled: isDisabled,
3718
+ onAddChangeGroup: onAddRow
3641
3719
  }), /*#__PURE__*/React__default["default"].createElement(reactSortablejs.ReactSortable, {
3642
3720
  tag: "div",
3643
3721
  list: fields,
@@ -3661,6 +3739,7 @@
3661
3739
  lastRow: fields.length === index + 1,
3662
3740
  disabled: isDisabled,
3663
3741
  onRemoveChangeGroup: onRemoverow,
3742
+ onAddChangeGroup: onAddRow,
3664
3743
  isChangeGroup: true,
3665
3744
  dragHandle: /*#__PURE__*/React__default["default"].createElement(DragHandle, {
3666
3745
  isDisabled: fields.length === 1 || isDisabled,
@@ -3668,20 +3747,10 @@
3668
3747
  index: index
3669
3748
  })
3670
3749
  });
3671
- })), !isDisabled && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(core.Grid, {
3672
- container: true,
3673
- item: true,
3674
- xs: 12,
3675
- justify: "flex-end"
3676
- }, /*#__PURE__*/React__default["default"].createElement(core.IconButton, {
3677
- "aria-label": "add",
3678
- "data-testid": "add-changeGroup",
3679
- onClick: onAddRow,
3680
- className: classes.addButton
3681
- }, /*#__PURE__*/React__default["default"].createElement(icons.Add, null))), /*#__PURE__*/React__default["default"].createElement(TafFormButtons, {
3750
+ })), !isDisabled && /*#__PURE__*/React__default["default"].createElement(TafFormButtons, {
3682
3751
  tafAction: tafAction,
3683
3752
  onTafFormButtonPress: onTafButtonPress
3684
- })), hiddenInputFields.map(function (field) {
3753
+ }), hiddenInputFields.map(function (field) {
3685
3754
  return /*#__PURE__*/React__default["default"].createElement(formFields.ReactHookFormHiddenInput, {
3686
3755
  name: field,
3687
3756
  key: field