From cdbf395aa226f0f8d7be59fbe73db0a1a9ec7e42 Mon Sep 17 00:00:00 2001 From: Youssef Date: Wed, 23 Feb 2022 01:29:28 +0100 Subject: [PATCH 01/14] fix: remove toast when clicking cancel --- .../src/component/addons/AddonForm/AddonForm.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/src/component/addons/AddonForm/AddonForm.jsx b/frontend/src/component/addons/AddonForm/AddonForm.jsx index d47e76c6d1..fce897feb1 100644 --- a/frontend/src/component/addons/AddonForm/AddonForm.jsx +++ b/frontend/src/component/addons/AddonForm/AddonForm.jsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect } from 'react'; +import { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; -import { TextField, FormControlLabel, Switch } from '@material-ui/core'; -import { FormButtons, styles as commonStyles } from '../../common'; +import { TextField, FormControlLabel, Switch, Button } from '@material-ui/core'; +import { styles as commonStyles } from '../../common'; import { trim } from '../../common/util'; import { AddonParameters } from './AddonParameters/AddonParameters'; import { AddonEvents } from './AddonEvents/AddonEvents'; @@ -149,7 +149,7 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {

{errors.general}

-
+
{ />
- + +
From 8429ba05a0b6dd9b4de9bf5a2f3279feee2d3ec7 Mon Sep 17 00:00:00 2001 From: Youssef Date: Wed, 23 Feb 2022 13:57:12 +0100 Subject: [PATCH 02/14] fix: update PR based on feedback --- frontend/src/component/addons/AddonForm/AddonForm.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/component/addons/AddonForm/AddonForm.jsx b/frontend/src/component/addons/AddonForm/AddonForm.jsx index fce897feb1..889d20c51f 100644 --- a/frontend/src/component/addons/AddonForm/AddonForm.jsx +++ b/frontend/src/component/addons/AddonForm/AddonForm.jsx @@ -79,7 +79,7 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => { setErrors({ ...errors, events: undefined }); }; - const handleCancel = () => { + const onCancel = () => { history.goBack(); }; @@ -149,7 +149,7 @@ export const AddonForm = ({ editMode, provider, addon, fetch }) => {

{errors.general}

-
+
{ - +
From 94ecaa80a80302ae0ee98e295e3ed394bdbd521a Mon Sep 17 00:00:00 2001 From: olav Date: Wed, 23 Feb 2022 15:08:44 +0100 Subject: [PATCH 03/14] refactor: improve feature toggle search state (#741) * refactor: rename createPersistentGlobalStateHook helper * refactor: move features filter state out of localStorage * refactor: show search state in page title * refactor: remove unused import * refactor: add a state chip to SearchField * refactor: improve var names --- .../ApplicationList/ApplicationList.tsx | 3 +- .../common/SearchField/SearchField.jsx | 59 --------------- .../common/SearchField/SearchField.tsx | 74 +++++++++++++++++++ .../component/common/SearchField/styles.js | 9 ++- .../FeatureToggleList/FeatureToggleList.jsx | 14 ++-- .../list-component-test.jsx.snap | 48 ++++++------ .../feature/FeatureToggleList/styles.js | 1 + .../FeatureStrategyEditable.tsx | 1 - frontend/src/hooks/useEventSettings.ts | 4 +- frontend/src/hooks/useFeaturesFilter.ts | 9 +-- frontend/src/hooks/useFeaturesSort.ts | 4 +- frontend/src/hooks/useGlobalState.ts | 23 ++++++ frontend/src/hooks/useLocationSettings.ts | 4 +- .../src/hooks/usePersistentGlobalState.ts | 2 +- 14 files changed, 149 insertions(+), 106 deletions(-) delete mode 100644 frontend/src/component/common/SearchField/SearchField.jsx create mode 100644 frontend/src/component/common/SearchField/SearchField.tsx create mode 100644 frontend/src/hooks/useGlobalState.ts diff --git a/frontend/src/component/application/ApplicationList/ApplicationList.tsx b/frontend/src/component/application/ApplicationList/ApplicationList.tsx index db6c5416f3..972743e70c 100644 --- a/frontend/src/component/application/ApplicationList/ApplicationList.tsx +++ b/frontend/src/component/application/ApplicationList/ApplicationList.tsx @@ -1,9 +1,8 @@ import { useMemo, useState } from 'react'; import { CircularProgress } from '@material-ui/core'; import { Warning } from '@material-ui/icons'; - import { AppsLinkList, styles as commonStyles } from '../../common'; -import SearchField from '../../common/SearchField/SearchField'; +import { SearchField } from 'component/common/SearchField/SearchField'; import PageContent from '../../common/PageContent/PageContent'; import HeaderTitle from '../../common/HeaderTitle'; import useApplications from '../../../hooks/api/getters/useApplications/useApplications'; diff --git a/frontend/src/component/common/SearchField/SearchField.jsx b/frontend/src/component/common/SearchField/SearchField.jsx deleted file mode 100644 index 87f7980e69..0000000000 --- a/frontend/src/component/common/SearchField/SearchField.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { useState } from 'react'; -import classnames from 'classnames'; -import PropTypes from 'prop-types'; -import { debounce } from 'debounce'; -import { InputBase } from '@material-ui/core'; -import SearchIcon from '@material-ui/icons/Search'; - -import { useStyles } from './styles'; - -function SearchField({ initialValue = '', updateValue, className = '' }) { - const styles = useStyles(); - - const [localValue, setLocalValue] = useState(initialValue); - const debounceUpdateValue = debounce(updateValue, 500); - - const handleChange = e => { - e.preventDefault(); - const v = e.target.value || ''; - setLocalValue(v); - debounceUpdateValue(v); - }; - - const handleKeyPress = e => { - if (e.key === 'Enter') { - updateValue(localValue); - } - }; - - const updateNow = () => { - updateValue(localValue); - }; - - return ( -
-
- - -
-
- ); -} - -SearchField.propTypes = { - value: PropTypes.string, - updateValue: PropTypes.func.isRequired, -}; - -export default SearchField; diff --git a/frontend/src/component/common/SearchField/SearchField.tsx b/frontend/src/component/common/SearchField/SearchField.tsx new file mode 100644 index 0000000000..333cc5a603 --- /dev/null +++ b/frontend/src/component/common/SearchField/SearchField.tsx @@ -0,0 +1,74 @@ +import React, { useState } from 'react'; +import classnames from 'classnames'; +import { debounce } from 'debounce'; +import { InputBase, Chip } from '@material-ui/core'; +import SearchIcon from '@material-ui/icons/Search'; +import { useStyles } from './styles'; +import ConditionallyRender from 'component/common/ConditionallyRender'; + +interface ISearchFieldProps { + updateValue: React.Dispatch>; + initialValue?: string; + className?: string; + showValueChip?: boolean; +} + +export const SearchField = ({ + updateValue, + initialValue = '', + className = '', + showValueChip, +}: ISearchFieldProps) => { + const styles = useStyles(); + const [localValue, setLocalValue] = useState(initialValue); + const debounceUpdateValue = debounce(updateValue, 500); + + const handleChange = (event: React.ChangeEvent) => { + event.preventDefault(); + const value = event.target.value || ''; + setLocalValue(value); + debounceUpdateValue(value); + }; + + const handleKeyPress = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + updateValue(localValue); + } + }; + + const updateNow = () => { + updateValue(localValue); + }; + + const onDelete = () => { + setLocalValue(''); + updateValue(''); + }; + + return ( +
+
+ + +
+ + } + /> +
+ ); +}; diff --git a/frontend/src/component/common/SearchField/styles.js b/frontend/src/component/common/SearchField/styles.js index 09a4e41d1f..ed7c9e480a 100644 --- a/frontend/src/component/common/SearchField/styles.js +++ b/frontend/src/component/common/SearchField/styles.js @@ -1,6 +1,12 @@ import { makeStyles } from '@material-ui/styles'; export const useStyles = makeStyles(theme => ({ + container: { + display: 'flex', + alignItems: 'center', + flexWrap: 'wrap', + gap: '1rem', + }, search: { display: 'flex', alignItems: 'center', @@ -8,9 +14,6 @@ export const useStyles = makeStyles(theme => ({ borderRadius: '25px', padding: '0.25rem 0.5rem', maxWidth: '450px', - [theme.breakpoints.down('sm')]: { - margin: '0 auto', - }, [theme.breakpoints.down('xs')]: { width: '100%', }, diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx index 7c5ea29409..a356fe3015 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx @@ -5,20 +5,15 @@ import { Link } from 'react-router-dom'; import { Button, IconButton, List, ListItem, Tooltip } from '@material-ui/core'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import { Add } from '@material-ui/icons'; - import FeatureToggleListItem from './FeatureToggleListItem'; -import SearchField from '../../common/SearchField/SearchField'; +import { SearchField } from '../../common/SearchField/SearchField'; import FeatureToggleListActions from './FeatureToggleListActions'; import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; import PageContent from '../../common/PageContent/PageContent'; import HeaderTitle from '../../common/HeaderTitle'; - import loadingFeatures from './loadingFeatures'; - import { CREATE_FEATURE } from '../../providers/AccessProvider/permissions'; - import AccessContext from '../../../contexts/AccessContext'; - import { useStyles } from './styles'; import ListPlaceholder from '../../common/ListPlaceholder/ListPlaceholder'; import { getCreateTogglePath } from '../../../utils/route-path-helpers'; @@ -101,7 +96,11 @@ const FeatureToggleList = ({ ); }; - const headerTitle = archive ? 'Archived Features' : 'Features'; + const headerTitle = filter.query + ? 'Search results' + : archive + ? 'Archived Features' + : 'Features'; return (
@@ -109,6 +108,7 @@ const FeatureToggleList = ({ -
+
@@ -20,7 +22,7 @@ exports[`renders correctly with one feature 1`] = ` />
@@ -31,7 +33,7 @@ exports[`renders correctly with one feature 1`] = ` onBlur={[Function]} onChange={[Function]} onFocus={[Function]} - placeholder="Search…" + placeholder="Search..." type="text" value="" /> @@ -55,29 +57,29 @@ exports[`renders correctly with one feature 1`] = ` } >

Features

    -
    +
    @@ -212,7 +216,7 @@ exports[`renders correctly with one feature without permissions 1`] = ` />
    @@ -223,7 +227,7 @@ exports[`renders correctly with one feature without permissions 1`] = ` onBlur={[Function]} onChange={[Function]} onFocus={[Function]} - placeholder="Search…" + placeholder="Search..." type="text" value="" /> @@ -247,29 +251,29 @@ exports[`renders correctly with one feature without permissions 1`] = ` } >

    Features

      ({ searchBarContainer: { marginBottom: '2rem', display: 'flex', + gap: '1rem', justifyContent: 'space-between', alignItems: 'center', [theme.breakpoints.down('xs')]: { diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx index 38fc7d70a6..0c8afb086c 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx @@ -15,7 +15,6 @@ import { Tooltip } from '@material-ui/core'; import ConditionallyRender from '../../../../../common/ConditionallyRender'; import { useStyles } from './FeatureStrategyEditable.styles'; import { Delete } from '@material-ui/icons'; -import { PRODUCTION } from '../../../../../../constants/environmentTypes'; import { DELETE_STRATEGY_ID, STRATEGY_ACCORDION_ID, diff --git a/frontend/src/hooks/useEventSettings.ts b/frontend/src/hooks/useEventSettings.ts index 21478a8bc6..d7c7c2ea95 100644 --- a/frontend/src/hooks/useEventSettings.ts +++ b/frontend/src/hooks/useEventSettings.ts @@ -1,5 +1,5 @@ import { getBasePath } from '../utils/format-path'; -import { createPersistentGlobalState } from './usePersistentGlobalState'; +import { createPersistentGlobalStateHook } from './usePersistentGlobalState'; import React from 'react'; export interface IEventSettings { @@ -21,7 +21,7 @@ const createInitialValue = (): IEventSettings => { return { showData: false }; }; -const useGlobalState = createPersistentGlobalState( +const useGlobalState = createPersistentGlobalStateHook( `${getBasePath()}:useEventSettings:v1`, createInitialValue() ); diff --git a/frontend/src/hooks/useFeaturesFilter.ts b/frontend/src/hooks/useFeaturesFilter.ts index 452f73911a..085d13622b 100644 --- a/frontend/src/hooks/useFeaturesFilter.ts +++ b/frontend/src/hooks/useFeaturesFilter.ts @@ -1,7 +1,6 @@ -import { IFeatureToggle } from '../interfaces/featureToggle'; +import { IFeatureToggle } from 'interfaces/featureToggle'; import React, { useMemo } from 'react'; -import { getBasePath } from '../utils/format-path'; -import { createPersistentGlobalState } from './usePersistentGlobalState'; +import { createGlobalStateHook } from 'hooks/useGlobalState'; export interface IFeaturesFilter { query?: string; @@ -16,8 +15,8 @@ export interface IFeaturesSortOutput { // Store the features filter state globally, and in localStorage. // When changing the format of IFeaturesFilter, change the version as well. -const useFeaturesFilterState = createPersistentGlobalState( - `${getBasePath()}:useFeaturesFilter:v1`, +const useFeaturesFilterState = createGlobalStateHook( + 'useFeaturesFilterState', { project: '*' } ); diff --git a/frontend/src/hooks/useFeaturesSort.ts b/frontend/src/hooks/useFeaturesSort.ts index 282916671a..a72c2fc295 100644 --- a/frontend/src/hooks/useFeaturesSort.ts +++ b/frontend/src/hooks/useFeaturesSort.ts @@ -1,7 +1,7 @@ import { IFeatureToggle } from '../interfaces/featureToggle'; import React, { useMemo } from 'react'; import { getBasePath } from '../utils/format-path'; -import { createPersistentGlobalState } from './usePersistentGlobalState'; +import { createPersistentGlobalStateHook } from './usePersistentGlobalState'; type FeaturesSortType = | 'name' @@ -29,7 +29,7 @@ export interface IFeaturesFilterSortOption { // Store the features sort state globally, and in localStorage. // When changing the format of IFeaturesSort, change the version as well. -const useFeaturesSortState = createPersistentGlobalState( +const useFeaturesSortState = createPersistentGlobalStateHook( `${getBasePath()}:useFeaturesSort:v1`, { type: 'name' } ); diff --git a/frontend/src/hooks/useGlobalState.ts b/frontend/src/hooks/useGlobalState.ts new file mode 100644 index 0000000000..37469ebd6a --- /dev/null +++ b/frontend/src/hooks/useGlobalState.ts @@ -0,0 +1,23 @@ +import React from 'react'; +import { createGlobalState } from 'react-hooks-global-state'; + +type UseGlobalState = () => [ + value: T, + setValue: React.Dispatch> +]; + +// Create a hook that stores global state (shared across all hook instances). +export const createGlobalStateHook = ( + key: string, + initialValue: T +): UseGlobalState => { + const container = createGlobalState<{ [key: string]: T }>({ + [key]: initialValue, + }); + + const setGlobalState = (value: React.SetStateAction) => { + container.setGlobalState(key, value); + }; + + return () => [container.useGlobalState(key)[0], setGlobalState]; +}; diff --git a/frontend/src/hooks/useLocationSettings.ts b/frontend/src/hooks/useLocationSettings.ts index 5ffa2d18de..5e74ff0240 100644 --- a/frontend/src/hooks/useLocationSettings.ts +++ b/frontend/src/hooks/useLocationSettings.ts @@ -1,5 +1,5 @@ import { getBasePath } from '../utils/format-path'; -import { createPersistentGlobalState } from './usePersistentGlobalState'; +import { createPersistentGlobalStateHook } from './usePersistentGlobalState'; import React from 'react'; export interface ILocationSettings { @@ -23,7 +23,7 @@ const createInitialValue = (): ILocationSettings => { return { locale: navigator.language }; }; -const useGlobalState = createPersistentGlobalState( +const useGlobalState = createPersistentGlobalStateHook( `${getBasePath()}:useLocationSettings:v1`, createInitialValue() ); diff --git a/frontend/src/hooks/usePersistentGlobalState.ts b/frontend/src/hooks/usePersistentGlobalState.ts index 1a06da3ffe..1f743a23ea 100644 --- a/frontend/src/hooks/usePersistentGlobalState.ts +++ b/frontend/src/hooks/usePersistentGlobalState.ts @@ -10,7 +10,7 @@ type UsePersistentGlobalState = () => [ // Create a hook that stores global state (shared across all hook instances). // The state is also persisted to localStorage and restored on page load. // The localStorage state is not synced between tabs. -export const createPersistentGlobalState = ( +export const createPersistentGlobalStateHook = ( key: string, initialValue: T ): UsePersistentGlobalState => { From 85f8977b523c200978dc6ebc4d94253af3a2b134 Mon Sep 17 00:00:00 2001 From: Fredrik Oseberg Date: Wed, 23 Feb 2022 15:09:24 +0100 Subject: [PATCH 04/14] 4.8.0-beta.9 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 376852644a..e783db8116 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "unleash-frontend", "description": "unleash your features", - "version": "4.8.0-beta.8", + "version": "4.8.0-beta.9", "keywords": [ "unleash", "feature toggle", From 3d93ed44d4b2ff2dd3546d217bdde677f260561b Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Wed, 23 Feb 2022 16:08:04 +0100 Subject: [PATCH 05/14] fix/search-header (#742) * fix/search-header * fix: update snapshots --- .../feature/FeatureToggleList/FeatureToggleList.jsx | 12 +++++++----- .../__snapshots__/list-component-test.jsx.snap | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx index a356fe3015..fad1e7b31d 100644 --- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx +++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx @@ -96,11 +96,13 @@ const FeatureToggleList = ({ ); }; - const headerTitle = filter.query - ? 'Search results' - : archive - ? 'Archived Features' - : 'Features'; + const searchResultsHeader = filter.query + ? `(${features.length} matches)` + : ''; + + const headerTitle = archive + ? `Archived Features ${searchResultsHeader}` + : `Features ${searchResultsHeader}`; return (
      diff --git a/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap b/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap index 54e3ff7713..3b925f0413 100644 --- a/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap +++ b/frontend/src/component/feature/FeatureToggleList/__tests__/__snapshots__/list-component-test.jsx.snap @@ -69,7 +69,7 @@ exports[`renders correctly with one feature 1`] = `

      - Features + Features

      - Features + Features
      Date: Wed, 23 Feb 2022 17:23:21 +0100 Subject: [PATCH 06/14] refactor: remove chart point filter (#743) --- .../FeatureMetrics/FeatureMetricsChart/createChartData.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts index 162437ad56..c5af93fdfb 100644 --- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts +++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartData.ts @@ -42,10 +42,8 @@ const createChartPoints = ( locationSettings: ILocationSettings, y: (m: IFeatureMetricsRaw) => number ): IPoint[] => { - const points = metrics.map(metric => ({ + return metrics.map(metric => ({ x: metric.timestamp, y: y(metric), })); - - return points.filter(point => point.y > 0); }; From 6d2ea5ebc61eb86bee8a15d38a749ab92dee460e Mon Sep 17 00:00:00 2001 From: Fredrik Oseberg Date: Wed, 23 Feb 2022 17:23:34 +0100 Subject: [PATCH 07/14] 4.8.0-beta.10 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index e783db8116..3c8ff8f589 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "unleash-frontend", "description": "unleash your features", - "version": "4.8.0-beta.9", + "version": "4.8.0-beta.10", "keywords": [ "unleash", "feature toggle", From 512b3d1e123662e7a3e154f44b9ba1a6f7ff2a99 Mon Sep 17 00:00:00 2001 From: Fredrik Oseberg Date: Thu, 24 Feb 2022 09:14:13 +0100 Subject: [PATCH 08/14] 4.8.0 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 3c8ff8f589..58380489b0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "unleash-frontend", "description": "unleash your features", - "version": "4.8.0-beta.10", + "version": "4.8.0", "keywords": [ "unleash", "feature toggle", From 19b16ed600cba610b9978ea4953d3a7681f332a9 Mon Sep 17 00:00:00 2001 From: olav Date: Fri, 25 Feb 2022 10:21:28 +0100 Subject: [PATCH 09/14] refactor: fix flaky Cypress tests (#746) * refactor: normalize spec names * refactor: make ESLint ignore dir * refactor: port specs to TS * refactor: wait for login redirects to finish * refactor: remove static wait timers * refactor: match any env name in interceptors * refactor: move config vars to the top * refactor: use longer IDs to avoid collisions * refactor: misc cleanup * refactor: disable screenshots and videos * refactor: disable prod guard in tests * refactor: wait for inputs before typing --- frontend/.github/workflows/e2e.auth.yml | 8 +- frontend/.github/workflows/e2e.feature.yml | 8 +- frontend/cypress.json | 4 +- .../auth/{auth.spec.js => auth.spec.ts} | 11 -- .../feature.spec.ts} | 153 +++++++----------- .../cypress/plugins/{index.js => index.ts} | 0 .../support/{commands.js => commands.ts} | 0 .../cypress/support/{index.js => index.ts} | 0 frontend/package.json | 5 +- .../FeatureStrategiesProductionGuard.tsx | 7 + .../user/UserProfile/UserProfile.tsx | 8 +- frontend/src/testIds.js | 1 + 12 files changed, 87 insertions(+), 118 deletions(-) rename frontend/cypress/integration/auth/{auth.spec.js => auth.spec.ts} (93%) rename frontend/cypress/integration/{feature-toggle/feature.spec.js => feature/feature.spec.ts} (76%) rename frontend/cypress/plugins/{index.js => index.ts} (100%) rename frontend/cypress/support/{commands.js => commands.ts} (100%) rename frontend/cypress/support/{index.js => index.ts} (100%) diff --git a/frontend/.github/workflows/e2e.auth.yml b/frontend/.github/workflows/e2e.auth.yml index 6270d196b9..441ef0fdfd 100644 --- a/frontend/.github/workflows/e2e.auth.yml +++ b/frontend/.github/workflows/e2e.auth.yml @@ -16,10 +16,10 @@ jobs: uses: actions/checkout@v2 - name: Run Cypress uses: cypress-io/github-action@v2 - with: - env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }},DEFAULT_ENV="development" + with: + env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }} config: baseUrl=${{ github.event.deployment_status.target_url }} record: true - spec: cypress/integration/auth/auth.spec.js + spec: cypress/integration/auth/auth.spec.ts env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} \ No newline at end of file + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/frontend/.github/workflows/e2e.feature.yml b/frontend/.github/workflows/e2e.feature.yml index cfa3af2438..c0826e6194 100644 --- a/frontend/.github/workflows/e2e.feature.yml +++ b/frontend/.github/workflows/e2e.feature.yml @@ -16,10 +16,10 @@ jobs: uses: actions/checkout@v2 - name: Run Cypress uses: cypress-io/github-action@v2 - with: - env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }},DEFAULT_ENV="development" + with: + env: AUTH_TOKEN=${{ secrets.UNLEASH_TOKEN }} config: baseUrl=${{ github.event.deployment_status.target_url }} record: true - spec: cypress/integration/feature-toggle/feature.spec.js + spec: cypress/integration/feature/feature.spec.ts env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} \ No newline at end of file + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} diff --git a/frontend/cypress.json b/frontend/cypress.json index e91af16203..ba6b740e2d 100644 --- a/frontend/cypress.json +++ b/frontend/cypress.json @@ -1,4 +1,6 @@ { "projectId": "tc2qff", - "defaultCommandTimeout": 12000 + "defaultCommandTimeout": 12000, + "screenshotOnRunFailure": false, + "video": false } diff --git a/frontend/cypress/integration/auth/auth.spec.js b/frontend/cypress/integration/auth/auth.spec.ts similarity index 93% rename from frontend/cypress/integration/auth/auth.spec.js rename to frontend/cypress/integration/auth/auth.spec.ts index 63500c543d..d05a936a4d 100644 --- a/frontend/cypress/integration/auth/auth.spec.js +++ b/frontend/cypress/integration/auth/auth.spec.ts @@ -1,15 +1,4 @@ -/* eslint-disable jest/no-conditional-expect */ /// -// Welcome to Cypress! -// -// This spec file contains a variety of sample tests -// for a todo list app that are designed to demonstrate -// the power of writing tests in Cypress. -// -// To learn more about how Cypress works and -// what makes it such an awesome testing tool, -// please read our getting started guide: -// https://on.cypress.io/introduction-to-cypress const username = 'test@test.com'; const password = 'qY70$NDcJNXA'; diff --git a/frontend/cypress/integration/feature-toggle/feature.spec.js b/frontend/cypress/integration/feature/feature.spec.ts similarity index 76% rename from frontend/cypress/integration/feature-toggle/feature.spec.js rename to frontend/cypress/integration/feature/feature.spec.ts index a808390cae..0e682af659 100644 --- a/frontend/cypress/integration/feature-toggle/feature.spec.js +++ b/frontend/cypress/integration/feature/feature.spec.ts @@ -1,82 +1,48 @@ -/* eslint-disable jest/no-conditional-expect */ /// -// Welcome to Cypress! -// -// This spec file contains a variety of sample tests -// for a todo list app that are designed to demonstrate -// the power of writing tests in Cypress. -// -// To learn more about how Cypress works and -// what makes it such an awesome testing tool, -// please read our getting started guide: -// https://on.cypress.io/introduction-to-cypress -let featureToggleName = ''; -let enterprise = false; +import { disableFeatureStrategiesProductionGuard } from '../../../src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard'; + +const randomId = String(Math.random()).split('.')[1]; +const featureToggleName = `unleash-e2e-${randomId}`; +const enterprise = Boolean(Cypress.env('ENTERPRISE')); +const passwordAuth = Cypress.env('PASSWORD_AUTH'); +const authToken = Cypress.env('AUTH_TOKEN'); +const baseUrl = Cypress.config().baseUrl; let strategyId = ''; -let defaultEnv = 'development'; - -describe('feature toggle', () => { - before(() => { - featureToggleName = `unleash-e2e-${Math.floor(Math.random() * 100)}`; - enterprise = Boolean(Cypress.env('ENTERPRISE')); - - const env = Cypress.env('DEFAULT_ENV'); - if (env) { - defaultEnv = env; - } - }); +describe('feature', () => { after(() => { - const authToken = Cypress.env('AUTH_TOKEN'); - cy.request({ method: 'DELETE', - url: `${ - Cypress.config().baseUrl - }/api/admin/features/${featureToggleName}`, - headers: { - Authorization: authToken, - }, + url: `${baseUrl}/api/admin/features/${featureToggleName}`, + headers: { Authorization: authToken }, }); - cy.request({ method: 'DELETE', - url: `${ - Cypress.config().baseUrl - }/api/admin/archive/${featureToggleName}`, - headers: { - Authorization: authToken, - }, + url: `${baseUrl}/api/admin/archive/${featureToggleName}`, + headers: { Authorization: authToken }, }); }); beforeEach(() => { - // Cypress starts out with a blank slate for each test - // so we must tell it to visit our website with the `cy.visit()` command. - // Since we want to visit the same URL at the start of all our tests, - // we include it in our beforeEach function so that it runs before each test - const passwordAuth = Cypress.env('PASSWORD_AUTH'); - enterprise = Boolean(Cypress.env('ENTERPRISE')); - + disableFeatureStrategiesProductionGuard(); cy.visit('/'); if (passwordAuth) { cy.get('[data-test="LOGIN_EMAIL_ID"]').type('test@test.com'); - cy.get('[data-test="LOGIN_PASSWORD_ID"]').type('qY70$NDcJNXA'); - cy.get("[data-test='LOGIN_BUTTON']").click(); } else { cy.get('[data-test=LOGIN_EMAIL_ID]').type('test@unleash-e2e.com'); cy.get('[data-test=LOGIN_BUTTON]').click(); } + + // Wait for the login redirects to complete. + cy.get('[data-test=HEADER_USER_AVATAR'); }); - it('Creates a feature toggle', () => { - if ( - document.querySelectorAll("[data-test='CLOSE_SPLASH']").length > 0 - ) { + it('can create a feature toggle', () => { + if (document.querySelector("[data-test='CLOSE_SPLASH']")) { cy.get("[data-test='CLOSE_SPLASH']").click(); } @@ -87,14 +53,13 @@ describe('feature toggle', () => { ); cy.get("[data-test='CF_NAME_ID'").type(featureToggleName); - cy.get("[data-test='CF_DESC_ID'").type('hellowrdada'); - + cy.get("[data-test='CF_DESC_ID'").type('hello-world'); cy.get("[data-test='CF_CREATE_BTN_ID']").click(); cy.wait('@createFeature'); cy.url().should('include', featureToggleName); }); - it('Gives an error if a toggle exists with the same name', () => { + it('gives an error if a toggle exists with the same name', () => { cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click(); cy.intercept('POST', '/api/admin/projects/default/features').as( @@ -102,16 +67,14 @@ describe('feature toggle', () => { ); cy.get("[data-test='CF_NAME_ID'").type(featureToggleName); - cy.get("[data-test='CF_DESC_ID'").type('hellowrdada'); - + cy.get("[data-test='CF_DESC_ID'").type('hello-world'); cy.get("[data-test='CF_CREATE_BTN_ID']").click(); - cy.get("[data-test='INPUT_ERROR_TEXT']").contains( 'A feature with this name already exists' ); }); - it('Gives an error if a toggle name is url unsafe', () => { + it('gives an error if a toggle name is url unsafe', () => { cy.get('[data-test=NAVIGATE_TO_CREATE_FEATURE').click(); cy.intercept('POST', '/api/admin/projects/default/features').as( @@ -119,17 +82,14 @@ describe('feature toggle', () => { ); cy.get("[data-test='CF_NAME_ID'").type('featureToggleUnsafe####$#//'); - cy.get("[data-test='CF_DESC_ID'").type('hellowrdada'); - + cy.get("[data-test='CF_DESC_ID'").type('hello-world'); cy.get("[data-test='CF_CREATE_BTN_ID']").click(); - cy.get("[data-test='INPUT_ERROR_TEXT']").contains( `"name" must be URL friendly` ); }); - it('Can add a gradual rollout strategy to the development environment', () => { - cy.wait(1000); + it('can add a gradual rollout strategy to the development environment', () => { cy.visit(`/projects/default/features/${featureToggleName}/strategies`); cy.get('[data-test=ADD_NEW_STRATEGY_ID]').click(); cy.get('[data-test=ADD_NEW_STRATEGY_CARD_BUTTON_ID-2').click(); @@ -147,10 +107,9 @@ describe('feature toggle', () => { cy.intercept( 'POST', - `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies`, + `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies`, req => { expect(req.body.name).to.equal('flexibleRollout'); - expect(req.body.parameters.groupId).to.equal(featureToggleName); expect(req.body.parameters.stickiness).to.equal('default'); expect(req.body.parameters.rollout).to.equal(30); @@ -172,7 +131,6 @@ describe('feature toggle', () => { }); it('can update a strategy in the development environment', () => { - cy.wait(1000); cy.visit(`/projects/default/features/${featureToggleName}/strategies`); cy.get('[data-test=STRATEGY_ACCORDION_ID-flexibleRollout').click(); @@ -188,7 +146,6 @@ describe('feature toggle', () => { .first() .click(); - let newGroupId = 'new-group-id'; cy.get('[data-test=FLEXIBLE_STRATEGY_GROUP_ID]') .first() .clear() @@ -196,9 +153,9 @@ describe('feature toggle', () => { cy.intercept( 'PUT', - `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies/${strategyId}`, + `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies/${strategyId}`, req => { - expect(req.body.parameters.groupId).to.equal(newGroupId); + expect(req.body.parameters.groupId).to.equal('new-group-id'); expect(req.body.parameters.stickiness).to.equal('sessionId'); expect(req.body.parameters.rollout).to.equal(60); @@ -219,12 +176,11 @@ describe('feature toggle', () => { }); it('can delete a strategy in the development environment', () => { - cy.wait(1000); cy.visit(`/projects/default/features/${featureToggleName}/strategies`); cy.intercept( 'DELETE', - `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies/${strategyId}`, + `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies/${strategyId}`, req => { req.continue(res => { expect(res.statusCode).to.equal(200); @@ -237,8 +193,7 @@ describe('feature toggle', () => { cy.wait('@deleteStrategy'); }); - it('Can add a userid strategy to the development environment', () => { - cy.wait(1000); + it('can add a userid strategy to the development environment', () => { cy.visit(`/projects/default/features/${featureToggleName}/strategies`); cy.get('[data-test=ADD_NEW_STRATEGY_ID]').click(); cy.get('[data-test=ADD_NEW_STRATEGY_CARD_BUTTON_ID-3').click(); @@ -260,7 +215,7 @@ describe('feature toggle', () => { cy.intercept( 'POST', - `/api/admin/projects/default/features/${featureToggleName}/environments/${defaultEnv}/strategies`, + `/api/admin/projects/default/features/${featureToggleName}/environments/*/strategies`, req => { expect(req.body.name).to.equal('userWithId'); @@ -282,11 +237,12 @@ describe('feature toggle', () => { cy.wait('@addStrategyToFeature'); }); - it('Can add two variant to the feature', () => { + it('can add two variant to the feature', () => { const variantName = 'my-new-variant'; const secondVariantName = 'my-second-variant'; - cy.wait(1000); + cy.visit(`/projects/default/features/${featureToggleName}/variants`); + cy.intercept( 'PATCH', `/api/admin/projects/default/features/${featureToggleName}/variants`, @@ -304,20 +260,23 @@ describe('feature toggle', () => { expect(req.body[1].value.name).to.equal(secondVariantName); } } - ).as('variantcreation'); - cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); - cy.get('[data-test=VARIANT_NAME_INPUT]').type(variantName); - cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); - cy.wait('@variantcreation'); - cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); - cy.get('[data-test=VARIANT_NAME_INPUT]').type(secondVariantName); - cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); - cy.wait('@variantcreation'); - }); - it('Can set weight to fixed value for one of the variants', () => { - cy.wait(1000); + ).as('variantCreation'); + cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); + cy.get('[data-test=VARIANT_NAME_INPUT]').click().type(variantName); + cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); + cy.wait('@variantCreation'); + cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); + cy.get('[data-test=VARIANT_NAME_INPUT]') + .click() + .type(secondVariantName); + cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); + cy.wait('@variantCreation'); + }); + + it('can set weight to fixed value for one of the variants', () => { cy.visit(`/projects/default/features/${featureToggleName}/variants`); + cy.get('[data-test=VARIANT_EDIT_BUTTON]').first().click(); cy.get('[data-test=VARIANT_NAME_INPUT]') .children() @@ -328,6 +287,7 @@ describe('feature toggle', () => { .find('input') .check(); cy.get('[data-test=VARIANT_WEIGHT_INPUT]').clear().type('15'); + cy.intercept( 'PATCH', `/api/admin/projects/default/features/${featureToggleName}/variants`, @@ -342,21 +302,23 @@ describe('feature toggle', () => { expect(req.body[2].path).to.match(/weight/); expect(req.body[2].value).to.equal(150); } - ).as('variantupdate'); + ).as('variantUpdate'); + cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); - cy.wait('@variantupdate'); + cy.wait('@variantUpdate'); cy.get('[data-test=VARIANT_WEIGHT]') .first() .should('have.text', '15 %'); }); - it(`can delete variant`, () => { + it('can delete variant', () => { const variantName = 'to-be-deleted'; - cy.wait(1000); + cy.visit(`/projects/default/features/${featureToggleName}/variants`); cy.get('[data-test=ADD_VARIANT_BUTTON]').click(); - cy.get('[data-test=VARIANT_NAME_INPUT]').type(variantName); + cy.get('[data-test=VARIANT_NAME_INPUT]').click().type(variantName); cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); + cy.intercept( 'PATCH', `/api/admin/projects/default/features/${featureToggleName}/variants`, @@ -365,6 +327,7 @@ describe('feature toggle', () => { expect(e.path).to.match(/\//); } ).as('delete'); + cy.get(`[data-test=VARIANT_DELETE_BUTTON_${variantName}]`).click(); cy.get('[data-test=DIALOGUE_CONFIRM_ID]').click(); cy.wait('@delete'); diff --git a/frontend/cypress/plugins/index.js b/frontend/cypress/plugins/index.ts similarity index 100% rename from frontend/cypress/plugins/index.js rename to frontend/cypress/plugins/index.ts diff --git a/frontend/cypress/support/commands.js b/frontend/cypress/support/commands.ts similarity index 100% rename from frontend/cypress/support/commands.js rename to frontend/cypress/support/commands.ts diff --git a/frontend/cypress/support/index.js b/frontend/cypress/support/index.ts similarity index 100% rename from frontend/cypress/support/index.js rename to frontend/cypress/support/index.ts diff --git a/frontend/package.json b/frontend/package.json index 58380489b0..e8beca23a6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -117,6 +117,9 @@ "no-restricted-globals": "off", "no-useless-computed-key": "off", "import/no-anonymous-default-export": "off" - } + }, + "ignorePatterns": [ + "cypress" + ] } } diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx index b58d3ab65b..fa50fe9fab 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx @@ -61,4 +61,11 @@ const FeatureStrategiesProductionGuard = ({ ); }; +export const disableFeatureStrategiesProductionGuard = () => { + localStorage.setItem( + FEATURE_STRATEGY_PRODUCTION_GUARD_SETTING, + String(true) + ); +}; + export default FeatureStrategiesProductionGuard; diff --git a/frontend/src/component/user/UserProfile/UserProfile.tsx b/frontend/src/component/user/UserProfile/UserProfile.tsx index d78a10ca77..45ab782e74 100644 --- a/frontend/src/component/user/UserProfile/UserProfile.tsx +++ b/frontend/src/component/user/UserProfile/UserProfile.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useState } from 'react'; import classnames from 'classnames'; import OutsideClickHandler from 'react-outside-click-handler'; - import { Avatar, Button } from '@material-ui/core'; import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; import { useStyles } from './UserProfile.styles'; @@ -9,6 +8,7 @@ import { useCommonStyles } from '../../../common.styles'; import UserProfileContent from './UserProfileContent/UserProfileContent'; import { IUser } from '../../../interfaces/user'; import { ILocationSettings } from '../../../hooks/useLocationSettings'; +import { HEADER_USER_AVATAR } from 'testIds'; interface IUserProfileProps { profile: IUser; @@ -69,7 +69,11 @@ const UserProfile = ({ role="button" disableRipple > - + Date: Fri, 25 Feb 2022 10:40:55 +0100 Subject: [PATCH 10/14] refactor: port date utils to TS (#720) --- .../api-token/ApiTokenList/ApiTokenList.tsx | 4 +- .../component/admin/invoice/InvoiceList.tsx | 4 +- .../UsersList/UserListItem/UserListItem.tsx | 13 +++--- .../ApplicationEdit/ApplicationEdit.tsx | 5 +-- .../ApplicationView/ApplicationView.tsx | 10 +++-- .../component/common/__tests__/util-test.jsx | 35 ---------------- frontend/src/component/common/util.js | 41 ------------------- .../FeatureToggleListNewItem/CreatedAt.tsx | 9 ++-- .../FeatureMetricsChart/createChartOptions.ts | 9 ++-- .../FeatureMetricsTable.tsx | 4 +- .../component/history/EventLog/EventLog.jsx | 7 +--- frontend/src/utils/format-date.ts | 34 +++++++++++++++ 12 files changed, 62 insertions(+), 113 deletions(-) delete mode 100644 frontend/src/component/common/__tests__/util-test.jsx create mode 100644 frontend/src/utils/format-date.ts diff --git a/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx b/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx index 7e97a831b1..7f6058b383 100644 --- a/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx +++ b/frontend/src/component/admin/api-token/ApiTokenList/ApiTokenList.tsx @@ -24,7 +24,6 @@ import { DELETE_API_TOKEN, } from '../../../providers/AccessProvider/permissions'; import { useStyles } from './ApiTokenList.styles'; -import { formatDateWithLocale } from '../../../common/util'; import Secret from './secret'; import { Delete, FileCopy } from '@material-ui/icons'; import Dialogue from '../../../common/Dialogue'; @@ -32,6 +31,7 @@ import { CREATE_API_TOKEN_BUTTON } from '../../../../testIds'; import { Alert } from '@material-ui/lab'; import copy from 'copy-to-clipboard'; import { useLocationSettings } from '../../../../hooks/useLocationSettings'; +import { formatDateYMD } from '../../../../utils/format-date'; interface IApiToken { createdAt: Date; @@ -146,7 +146,7 @@ export const ApiTokenList = () => { align="left" className={styles.hideSM} > - {formatDateWithLocale( + {formatDateYMD( item.createdAt, locationSettings.locale )} diff --git a/frontend/src/component/admin/invoice/InvoiceList.tsx b/frontend/src/component/admin/invoice/InvoiceList.tsx index adf6d589b9..8e766ef5ea 100644 --- a/frontend/src/component/admin/invoice/InvoiceList.tsx +++ b/frontend/src/component/admin/invoice/InvoiceList.tsx @@ -8,7 +8,6 @@ import { Button, } from '@material-ui/core'; import OpenInNew from '@material-ui/icons/OpenInNew'; -import { formatDateWithLocale } from '../../common/util'; import PageContent from '../../common/PageContent'; import HeaderTitle from '../../common/HeaderTitle'; import ConditionallyRender from '../../common/ConditionallyRender'; @@ -16,6 +15,7 @@ import { formatApiPath } from '../../../utils/format-path'; import useInvoices from '../../../hooks/api/getters/useInvoices/useInvoices'; import { IInvoice } from '../../../interfaces/invoice'; import { useLocationSettings } from '../../../hooks/useLocationSettings'; +import { formatDateYMD } from '../../../utils/format-date'; const PORTAL_URL = formatApiPath('api/admin/invoices/portal'); @@ -87,7 +87,7 @@ const InvoiceList = () => { style={{ textAlign: 'left' }} > {item.dueDate && - formatDateWithLocale( + formatDateYMD( item.dueDate, locationSettings.locale )} diff --git a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx index 6710630abc..7cdcb5f05c 100644 --- a/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx +++ b/frontend/src/component/admin/users/UsersList/UserListItem/UserListItem.tsx @@ -1,20 +1,20 @@ import { - TableRow, - TableCell, Avatar, IconButton, + TableCell, + TableRow, Typography, } from '@material-ui/core'; -import { Edit, Lock, Delete } from '@material-ui/icons'; +import { Delete, Edit, Lock } from '@material-ui/icons'; import { SyntheticEvent, useContext } from 'react'; import { ADMIN } from '../../../../providers/AccessProvider/permissions'; import ConditionallyRender from '../../../../common/ConditionallyRender'; -import { formatDateWithLocale } from '../../../../common/util'; import AccessContext from '../../../../../contexts/AccessContext'; import { IUser } from '../../../../../interfaces/user'; import { useStyles } from './UserListItem.styles'; import { useHistory } from 'react-router-dom'; import { ILocationSettings } from '../../../../../hooks/useLocationSettings'; +import { formatDateYMD } from '../../../../../utils/format-date'; interface IUserListItemProps { user: IUser; @@ -51,10 +51,7 @@ const UserListItem = ({ - {formatDateWithLocale( - user.createdAt, - locationSettings.locale - )} + {formatDateYMD(user.createdAt, locationSettings.locale)} diff --git a/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx b/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx index 4a8d6e0a04..2f5b9ddb39 100644 --- a/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx +++ b/frontend/src/component/application/ApplicationEdit/ApplicationEdit.tsx @@ -10,7 +10,6 @@ import { } from '@material-ui/core'; import { Link as LinkIcon } from '@material-ui/icons'; import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; -import { formatDateWithLocale } from '../../common/util'; import { UPDATE_APPLICATION } from '../../providers/AccessProvider/permissions'; import { ApplicationView } from '../ApplicationView/ApplicationView'; import { ApplicationUpdate } from '../ApplicationUpdate/ApplicationUpdate'; @@ -25,6 +24,7 @@ import { useHistory, useParams } from 'react-router-dom'; import { useLocationSettings } from '../../../hooks/useLocationSettings'; import useToast from '../../../hooks/useToast'; import PermissionButton from '../../common/PermissionButton/PermissionButton'; +import { formatDateYMD } from '../../../utils/format-date'; export const ApplicationEdit = () => { const history = useHistory(); @@ -42,8 +42,7 @@ export const ApplicationEdit = () => { setShowDialog(!showDialog); }; - const formatDate = (v: string) => - formatDateWithLocale(v, locationSettings.locale); + const formatDate = (v: string) => formatDateYMD(v, locationSettings.locale); const onDeleteApplication = async (evt: Event) => { evt.preventDefault(); diff --git a/frontend/src/component/application/ApplicationView/ApplicationView.tsx b/frontend/src/component/application/ApplicationView/ApplicationView.tsx index ee17b0875e..dffc1bb422 100644 --- a/frontend/src/component/application/ApplicationView/ApplicationView.tsx +++ b/frontend/src/component/application/ApplicationView/ApplicationView.tsx @@ -23,13 +23,16 @@ import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyR import { getTogglePath } from '../../../utils/route-path-helpers'; import useApplication from '../../../hooks/api/getters/useApplication/useApplication'; import AccessContext from '../../../contexts/AccessContext'; -import { formatFullDateTimeWithLocale } from '../../common/util'; +import { formatDateYMDHMS } from '../../../utils/format-date'; +import { useLocationSettings } from '../../../hooks/useLocationSettings'; export const ApplicationView = () => { const { hasAccess } = useContext(AccessContext); const { name } = useParams<{ name: string }>(); const { application } = useApplication(name); + const { locationSettings } = useLocationSettings(); const { instances, strategies, seenToggles } = application; + const notFoundListItem = ({ createUrl, name, @@ -195,8 +198,9 @@ export const ApplicationView = () => { {clientIp} last seen at{' '} - {formatFullDateTimeWithLocale( - lastSeen + {formatDateYMDHMS( + lastSeen, + locationSettings.locale )} diff --git a/frontend/src/component/common/__tests__/util-test.jsx b/frontend/src/component/common/__tests__/util-test.jsx deleted file mode 100644 index 7678c294a8..0000000000 --- a/frontend/src/component/common/__tests__/util-test.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { formatFullDateTimeWithLocale } from '../util'; - -test.skip('formats dates correctly', () => { - expect(formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'UTC')).toEqual( - '2017-02-23 14:56:49' - ); - expect( - formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'Europe/Paris') - ).toEqual('2017-02-23 15:56:49'); - expect( - formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'Europe/Oslo') - ).toEqual('2017-02-23 15:56:49'); - expect( - formatFullDateTimeWithLocale(1487861809466, 'nb-NO', 'Europe/London') - ).toEqual('2017-02-23 14:56:49'); - expect( - formatFullDateTimeWithLocale(1487861809466, 'en-GB', 'Europe/Paris') - ).toEqual('02/23/2017, 3:56:49 PM'); - expect( - formatFullDateTimeWithLocale(1487861809466, 'en-GB', 'Europe/Oslo') - ).toEqual('02/23/2017, 3:56:49 PM'); - expect( - formatFullDateTimeWithLocale(1487861809466, 'en-GB', 'Europe/London') - ).toEqual('02/23/2017, 2:56:49 PM'); - - expect(formatFullDateTimeWithLocale(1487861809466, 'nb-NO')).toEqual( - expect.stringMatching(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/) - ); - expect(formatFullDateTimeWithLocale(1487861809466, 'en-GB')).toEqual( - expect.stringContaining('02/23/2017') - ); - expect(formatFullDateTimeWithLocale(1487861809466, 'en-US')).toEqual( - expect.stringContaining('02/23/2017') - ); -}); diff --git a/frontend/src/component/common/util.js b/frontend/src/component/common/util.js index 28ee9b27ef..a538005dd6 100644 --- a/frontend/src/component/common/util.js +++ b/frontend/src/component/common/util.js @@ -1,26 +1,6 @@ import { weightTypes } from '../feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/enums'; import differenceInDays from 'date-fns/differenceInDays'; -const dateTimeOptions = { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', -}; - -const dateOptions = { - day: '2-digit', - month: '2-digit', - year: 'numeric', -}; - -const timeOptions = { - hour: '2-digit', - minute: '2-digit', -}; - export const filterByFlags = flags => r => { if (r.flag && !flags[r.flag]) { return false; @@ -32,27 +12,6 @@ export const scrollToTop = () => { window.scrollTo(0, 0); }; -export const formatFullDateTimeWithLocale = (v, locale, tz) => { - if (tz) { - dateTimeOptions.timeZone = tz; - } - return new Date(v).toLocaleString(locale, dateTimeOptions); -}; - -export const formatDateWithLocale = (v, locale, tz) => { - if (tz) { - dateTimeOptions.timeZone = tz; - } - return new Date(v).toLocaleString(locale, dateOptions); -}; - -export const formatTimeWithLocale = (v, locale, tz) => { - if (tz) { - dateTimeOptions.timeZone = tz; - } - return new Date(v).toLocaleString(locale, timeOptions); -}; - export const trim = value => { if (value && value.trim) { return value.trim(); diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx index d20eb7aa44..f7ba855579 100644 --- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx +++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/CreatedAt.tsx @@ -1,9 +1,6 @@ import { Tooltip } from '@material-ui/core'; -import { - formatDateWithLocale, - formatFullDateTimeWithLocale, -} from '../../../common/util'; import { useLocationSettings } from '../../../../hooks/useLocationSettings'; +import { formatDateYMD, formatDateYMDHMS } from '../../../../utils/format-date'; interface CreatedAtProps { time: Date; @@ -14,12 +11,12 @@ const CreatedAt = ({ time }: CreatedAtProps) => { return ( - {formatDateWithLocale(time, locationSettings.locale)} + {formatDateYMD(time, locationSettings.locale)} ); }; diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts index c40c901dd6..4f0f46a68f 100644 --- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts +++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsChart/createChartOptions.ts @@ -1,9 +1,9 @@ import { ILocationSettings } from '../../../../../hooks/useLocationSettings'; import 'chartjs-adapter-date-fns'; import { ChartOptions, defaults } from 'chart.js'; -import { formatTimeWithLocale } from '../../../../common/util'; import { IFeatureMetricsRaw } from '../../../../../interfaces/featureToggle'; import theme from '../../../../../themes/main-theme'; +import { formatDateHM } from '../../../../../utils/format-date'; export const createChartOptions = ( metrics: IFeatureMetricsRaw[], @@ -30,7 +30,7 @@ export const createChartOptions = ( usePointStyle: true, callbacks: { title: items => - formatTimeWithLocale( + formatDateHM( items[0].parsed.x, locationSettings.locale ), @@ -73,10 +73,7 @@ export const createChartOptions = ( grid: { display: false }, ticks: { callback: (_, i, data) => - formatTimeWithLocale( - data[i].value, - locationSettings.locale - ), + formatDateHM(data[i].value, locationSettings.locale), }, }, }, diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx index 186b20dd4e..d877d1fc25 100644 --- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsTable/FeatureMetricsTable.tsx @@ -9,8 +9,8 @@ import { useTheme, } from '@material-ui/core'; import { useLocationSettings } from '../../../../../hooks/useLocationSettings'; -import { formatFullDateTimeWithLocale } from '../../../../common/util'; import { useMemo } from 'react'; +import { formatDateYMDHMS } from 'utils/format-date'; export const FEATURE_METRICS_TABLE_ID = 'feature-metrics-table-id'; @@ -48,7 +48,7 @@ export const FeatureMetricsTable = ({ metrics }: IFeatureMetricsTableProps) => { {sortedMetrics.map(metric => ( - {formatFullDateTimeWithLocale( + {formatDateYMDHMS( metric.timestamp, locationSettings.locale )} diff --git a/frontend/src/component/history/EventLog/EventLog.jsx b/frontend/src/component/history/EventLog/EventLog.jsx index d5bbc04b78..79189697d4 100644 --- a/frontend/src/component/history/EventLog/EventLog.jsx +++ b/frontend/src/component/history/EventLog/EventLog.jsx @@ -1,14 +1,11 @@ import { List, Switch, FormControlLabel } from '@material-ui/core'; import PropTypes from 'prop-types'; - -import { formatFullDateTimeWithLocale } from '../../common/util'; - import EventJson from './EventJson/EventJson'; import PageContent from '../../common/PageContent/PageContent'; import HeaderTitle from '../../common/HeaderTitle'; import EventCard from './EventCard/EventCard'; - import { useStyles } from './EventLog.styles.js'; +import { formatDateYMDHMS } from '../../../utils/format-date'; const EventLog = ({ title, @@ -23,7 +20,7 @@ const EventLog = ({ setEventSettings({ showData: !eventSettings.showData }); }; const formatFulldateTime = v => { - return formatFullDateTimeWithLocale(v, locationSettings.locale); + return formatDateYMDHMS(v, locationSettings.locale); }; if (!history || history.length < 0) { diff --git a/frontend/src/utils/format-date.ts b/frontend/src/utils/format-date.ts new file mode 100644 index 0000000000..0bc10ff721 --- /dev/null +++ b/frontend/src/utils/format-date.ts @@ -0,0 +1,34 @@ +export const formatDateYMDHMS = ( + date: number | string | Date, + locale: string +): string => { + return new Date(date).toLocaleString(locale, { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); +}; + +export const formatDateYMD = ( + date: number | string | Date, + locale: string +): string => { + return new Date(date).toLocaleString(locale, { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }); +}; + +export const formatDateHM = ( + date: number | string | Date, + locale: string +): string => { + return new Date(date).toLocaleString(locale, { + hour: '2-digit', + minute: '2-digit', + }); +}; From 3959e846e825dda7ca85557d9bb8a7814cd0942f Mon Sep 17 00:00:00 2001 From: olav Date: Fri, 25 Feb 2022 10:55:39 +0100 Subject: [PATCH 11/14] refactor: fix misc TS errors (#729) * refactor: update test deps * refactor: remove unused ts-expect-error annotations * refactor: add missing arg and return types * refactor: the loading prop is optional * refactor: add missing arg and return types * reafactor: fix value arg type * refactor: fix missing array type * refactor: the parameters field is an array * refactor: use undefined instead of null in state * refactor: add missing params type * refactor: add missing children prop * refactor: add missing array type * refactor: add missing React imports * refactor: use correct IProjectEnvironment type * refactor: type errors as unknown * refactor: the index prop is required * refactor: fix date prop type * refactor: fix tooltip placement prop type * refactor: fix environments state type * refactor: add missing arg types * refactor: add guard for undefined field * refactor: fix ChangePassword prop types * refactor: fix MUI import paths * refactor: add missing arg type * refactor: fix showDialog prop type * refactor: remove unused openUpdateDialog prop * refactor: add missing non-null assertion * refactor: remove unused types prop * refactor: stricten API error handler types * refactor: add missing undefined check * refactor: add missing IProject id field * refactor: fix ConditionallyRender condition prop types * refactor: remove unused args * refactor: add AddVariant prop types * refactor: add types to UIContext * refactor: fix event arg type * refactor: add missing default impressionData field * refactor: fix handleDeleteEnvironment prop args * refactor: fix IFeatureMetrics field requirements * refactor: add missing element types to ConditionallyRender * refactor: remove unused ProjectAccess projectId prop * refactor: add missing undefined check * refactor: fix getCreateTogglePath arg type * refactor: add missing IStrategyPayload import * refactor: remove unused user arg * refactor: add missing event arg type * refactor: add missing style object types * refactor: improve userApiErrors prop type * refactor: the Dialogue onClose prop is optional * refactor: fix the AddonEvents setEventValue prop type --- frontend/package.json | 7 +- .../AddonForm/AddonEvents/AddonEvents.tsx | 4 +- .../component/addons/AddonList/AddonList.tsx | 4 +- .../ConfiguredAddons/ConfiguredAddons.tsx | 5 +- .../CreateApiToken/CreateApiToken.tsx | 5 +- .../admin/auth/GoogleAuth/GoogleAuth.tsx | 4 +- .../admin/auth/OidcAuth/OidcAuth.tsx | 4 +- .../admin/auth/PasswordAuth/PasswordAuth.tsx | 4 +- .../admin/auth/SamlAuth/SamlAuth.tsx | 4 +- .../CreateProjectRole/CreateProjectRole.tsx | 5 +- .../EditProjectRole/EditProjectRole.tsx | 5 +- .../ProjectRoleForm/ProjectRoleForm.tsx | 5 +- .../ProjectRoleDeleteConfirm.styles.ts | 2 +- .../ProjectRoleList/ProjectRoleList.tsx | 5 +- .../ProjectRoleListItem.tsx | 5 +- .../ProjectRoles/ProjectRoles.styles.ts | 2 +- .../ConfirmUserEmail.styles.ts | 2 +- .../admin/users/CreateUser/CreateUser.tsx | 5 +- .../admin/users/EditUser/EditUser.tsx | 5 +- .../component/admin/users/UserAdmin.styles.ts | 2 +- .../ChangePassword/ChangePassword.tsx | 12 +- .../users/UsersList/DeleteUser/DeleteUser.tsx | 8 +- .../UsersList/UserListItem/UserListItem.tsx | 2 - .../admin/users/UsersList/UsersList.tsx | 7 +- .../ApplicationEdit/ApplicationEdit.tsx | 11 +- .../ApplicationUpdate/ApplicationUpdate.tsx | 7 +- .../ApplicationView/ApplicationView.tsx | 9 +- .../common/AnimateOnMount/AnimateOnMount.tsx | 4 +- .../component/common/ApiError/ApiError.tsx | 1 + .../ConditionallyRender.tsx | 10 +- .../component/common/Dialogue/Dialogue.tsx | 12 +- .../EnvironmentSplashPage.tsx | 2 + .../common/FormTemplate/FormTemplate.tsx | 1 + .../component/common/Gradient/Gradient.tsx | 2 + frontend/src/component/common/Input/Input.tsx | 3 +- .../src/component/common/NoItems/NoItems.tsx | 1 + .../common/PaginateUI/PaginateUI.tsx | 2 +- .../common/PasswordField/PasswordField.tsx | 2 +- .../PermissionButton/PermissionButton.tsx | 2 +- .../PermissionIconButton.tsx | 2 +- .../ResponsiveButton/ResponsiveButton.tsx | 1 + .../src/component/common/Splash/Splash.tsx | 5 +- .../component/common/TagSelect/TagSelect.tsx | 2 +- .../common/ToastRenderer/Toast/Toast.tsx | 8 +- .../common/ToastRenderer/ToastRenderer.tsx | 8 +- .../context/ContextList/ContextList.jsx | 7 +- .../context/CreateContext/CreateContext.tsx | 7 +- .../context/EditContext/EditContext.tsx | 7 +- .../CreateEnvironment/CreateEnvironment.tsx | 5 +- .../EditEnvironment/EditEnvironment.tsx | 7 +- .../EnvironmentTypeSelector.tsx | 3 +- .../EnvironmentDeleteConfirm.tsx | 2 +- .../EnvironmentList/EnvironmentList.tsx | 21 ++- .../feature/CreateFeature/CreateFeature.tsx | 5 +- .../feature/EditFeature/EditFeature.tsx | 5 +- .../FeatureToggleListNewItem.tsx | 10 +- .../AddTagDialog/AddTagDialog.tsx | 11 +- .../FeatureOverviewEnvSwitch.tsx | 8 +- .../FeatureOverviewEnvironmentStrategy.tsx | 2 +- .../FeatureOverviewTags.tsx | 9 +- .../FeatureSettingsMetadata.tsx | 7 +- .../FeatureSettingsProject.tsx | 13 +- .../FeatureStatus/FeatureStatus.tsx | 9 +- .../FeatureStrategiesConfigure.tsx | 5 +- .../FeatureStrategiesEnvironmentList.tsx | 2 - .../useFeatureStrategiesEnvironmentList.ts | 15 +- .../FeatureStrategiesProductionGuard.tsx | 2 +- .../FeatureStrategyEditable.tsx | 4 +- .../FeatureStrategyCard.tsx | 2 +- .../FeatureStrategyAccordionBody.tsx | 2 +- .../GeneralStrategy/GeneralStrategy.tsx | 8 +- .../common/RolloutSlider/RolloutSlider.tsx | 1 + .../StrategyConstraints.tsx | 10 +- .../StrategyInputList/StrategyInputList.tsx | 2 +- .../common/UserWithIdStrategy/UserWithId.tsx | 2 +- .../AddFeatureVariant/AddFeatureVariant.tsx | 57 +++--- .../FeatureVariantsList.tsx | 17 +- .../FeatureVariantsListItem.tsx | 6 +- .../feature/FeatureView/FeatureView.tsx | 2 +- .../RedirectFeatureView.tsx | 8 +- .../Project/CreateProject/CreateProject.tsx | 5 +- .../Project/EditProject/EditProject.tsx | 5 +- .../src/component/project/Project/Project.tsx | 2 +- .../ProjectFeatureToggles.tsx | 2 +- .../Project/ProjectForm/ProjectForm.style.ts | 1 - .../ProjectAccessList/ProjectAccessList.tsx | 5 +- .../ProjectAccessListItem.tsx | 7 +- .../EnvironmentDisableConfirm.tsx | 4 +- .../ProjectEnvironment/ProjectEnvironment.tsx | 8 +- .../providers/UIProvider/UIProvider.tsx | 15 +- .../StrategiesList/StrategiesList.styles.ts | 2 +- .../StrategiesList/StrategiesList.tsx | 21 ++- .../strategies/StrategyView/StrategyView.tsx | 2 +- .../TogglesLinkList/TogglesLinkList.tsx | 5 +- .../tags/CreateTagType/CreateTagType.tsx | 5 +- .../tags/EditTagType/EditTagType.tsx | 5 +- .../tags/TagTypeList/TagTypeList.jsx | 5 +- .../src/component/user/DemoAuth/DemoAuth.jsx | 5 +- .../component/user/SimpleAuth/SimpleAuth.jsx | 5 +- .../UserProfile/EditProfile/EditProfile.tsx | 2 +- .../UserProfileContent/UserProfileContent.tsx | 6 - .../ResetPasswordForm/ResetPasswordForm.tsx | 10 +- frontend/src/contexts/UIContext.ts | 40 ++-- .../actions/useAdminUsersApi/errorHandlers.ts | 77 ++++---- .../src/hooks/api/actions/useApi/useApi.ts | 40 ++-- .../api/getters/useFeature/defaultFeature.ts | 1 + .../useFeatureMetrics/useFeatureMetrics.ts | 5 +- frontend/src/hooks/useToast.tsx | 26 +-- frontend/src/interfaces/featureToggle.ts | 4 +- frontend/src/interfaces/project.ts | 1 + frontend/src/interfaces/strategy.ts | 3 +- frontend/src/interfaces/toast.ts | 10 + frontend/src/themes/main-theme.ts | 4 + frontend/src/utils/api-utils.ts | 37 ++-- .../src/utils/project-filter-generator.ts | 9 +- .../src/utils/resolve-default-param-value.ts | 5 +- frontend/src/utils/route-path-helpers.ts | 4 +- frontend/yarn.lock | 177 ++++++++++++------ 118 files changed, 595 insertions(+), 466 deletions(-) create mode 100644 frontend/src/interfaces/toast.ts diff --git a/frontend/package.json b/frontend/package.json index e8beca23a6..db0b37539f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,6 +42,7 @@ "@material-ui/core": "4.12.3", "@material-ui/icons": "4.11.2", "@material-ui/lab": "4.0.0-alpha.60", + "@testing-library/dom": "^8.11.3", "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.3", "@testing-library/user-event": "13.5.0", @@ -49,7 +50,7 @@ "@types/deep-diff": "1.0.1", "@types/jest": "27.4.1", "@types/lodash.clonedeep": "4.5.6", - "@types/node": "14.18.12", + "@types/node": "17.0.18", "@types/react": "17.0.39", "@types/react-dom": "17.0.11", "@types/react-outside-click-handler": "1.3.1", @@ -63,7 +64,7 @@ "copy-to-clipboard": "3.3.1", "craco": "0.0.3", "css-loader": "6.6.0", - "cypress": "8.7.0", + "cypress": "9.5.0", "date-fns": "2.28.0", "debounce": "1.2.1", "deep-diff": "1.0.2", @@ -82,7 +83,7 @@ "react-outside-click-handler": "1.3.0", "react-router-dom": "5.3.0", "react-scripts": "4.0.3", - "react-test-renderer": "16.14.0", + "react-test-renderer": "17.0.2", "react-timeago": "6.2.1", "sass": "1.49.8", "swr": "1.2.2", diff --git a/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx b/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx index 3beb70432c..8e70661277 100644 --- a/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx +++ b/frontend/src/component/addons/AddonForm/AddonEvents/AddonEvents.tsx @@ -7,7 +7,9 @@ import { IAddonProvider } from '../../../../interfaces/addons'; interface IAddonProps { provider: IAddonProvider; checkedEvents: string[]; - setEventValue: (name: string) => void; + setEventValue: ( + name: string + ) => (event: React.ChangeEvent) => void; error: Record; } diff --git a/frontend/src/component/addons/AddonList/AddonList.tsx b/frontend/src/component/addons/AddonList/AddonList.tsx index 8d96e5c3d0..a5a43b0812 100644 --- a/frontend/src/component/addons/AddonList/AddonList.tsx +++ b/frontend/src/component/addons/AddonList/AddonList.tsx @@ -1,4 +1,4 @@ -import { ReactElement } from 'react'; +import React, { ReactElement } from 'react'; import { ConfiguredAddons } from './ConfiguredAddons/ConfiguredAddons'; import { AvailableAddons } from './AvailableAddons/AvailableAddons'; import { Avatar } from '@material-ui/core'; @@ -12,7 +12,7 @@ import dataDogIcon from '../../../assets/icons/datadog.svg'; import { formatAssetPath } from '../../../utils/format-path'; import useAddons from '../../../hooks/api/getters/useAddons/useAddons'; -const style = { +const style: React.CSSProperties = { width: '40px', height: '40px', marginRight: '16px', diff --git a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx b/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx index 78ef5188cc..7defb1f187 100644 --- a/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx +++ b/frontend/src/component/addons/AddonList/ConfiguredAddons/ConfiguredAddons.tsx @@ -21,6 +21,7 @@ import AccessContext from '../../../../contexts/AccessContext'; import { IAddon } from '../../../../interfaces/addons'; import PermissionIconButton from '../../../common/PermissionIconButton/PermissionIconButton'; import Dialogue from '../../../common/Dialogue'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; interface IConfigureAddonsProps { getAddonIcon: (name: string) => ReactElement; @@ -59,8 +60,8 @@ export const ConfiguredAddons = ({ getAddonIcon }: IConfigureAddonsProps) => { title: 'Success', text: 'Addon state switched successfully', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/admin/api-token/CreateApiToken/CreateApiToken.tsx b/frontend/src/component/admin/api-token/CreateApiToken/CreateApiToken.tsx index 85f2ff0e0e..a1a31d6035 100644 --- a/frontend/src/component/admin/api-token/CreateApiToken/CreateApiToken.tsx +++ b/frontend/src/component/admin/api-token/CreateApiToken/CreateApiToken.tsx @@ -10,6 +10,7 @@ import { ADMIN } from '../../../providers/AccessProvider/permissions'; import { ConfirmToken } from '../ConfirmToken/ConfirmToken'; import { useState } from 'react'; import { scrollToTop } from '../../../common/util'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; export const CreateApiToken = () => { const { setToastApiError } = useToast(); @@ -49,8 +50,8 @@ export const CreateApiToken = () => { setToken(api.secret); setShowConfirm(true); }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx b/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx index f9860a1fe0..43c9712e6b 100644 --- a/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx +++ b/frontend/src/component/admin/auth/GoogleAuth/GoogleAuth.tsx @@ -68,8 +68,8 @@ export const GoogleAuth = () => { title: 'Settings stored', type: 'success', }); - } catch (err) { - setToastApiError(formatUnknownError(err)); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx index c32b10da00..9fa252e6d9 100644 --- a/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx +++ b/frontend/src/component/admin/auth/OidcAuth/OidcAuth.tsx @@ -79,8 +79,8 @@ export const OidcAuth = () => { title: 'Settings stored', type: 'success', }); - } catch (err) { - setToastApiError(formatUnknownError(err)); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/admin/auth/PasswordAuth/PasswordAuth.tsx b/frontend/src/component/admin/auth/PasswordAuth/PasswordAuth.tsx index 068563b2e6..cef119c529 100644 --- a/frontend/src/component/admin/auth/PasswordAuth/PasswordAuth.tsx +++ b/frontend/src/component/admin/auth/PasswordAuth/PasswordAuth.tsx @@ -51,8 +51,8 @@ export const PasswordAuth = () => { type: 'success', show: true, }); - } catch (err) { - setToastApiError(formatUnknownError(err)); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); setDisablePasswordAuth(config.disabled); } }; diff --git a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx index 26f8c7a293..5120ddaa57 100644 --- a/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx +++ b/frontend/src/component/admin/auth/SamlAuth/SamlAuth.tsx @@ -75,8 +75,8 @@ export const SamlAuth = () => { title: 'Settings stored', type: 'success', }); - } catch (err) { - setToastApiError(formatUnknownError(err)); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/admin/project-roles/CreateProjectRole/CreateProjectRole.tsx b/frontend/src/component/admin/project-roles/CreateProjectRole/CreateProjectRole.tsx index 78048e0ab0..97835a132f 100644 --- a/frontend/src/component/admin/project-roles/CreateProjectRole/CreateProjectRole.tsx +++ b/frontend/src/component/admin/project-roles/CreateProjectRole/CreateProjectRole.tsx @@ -7,6 +7,7 @@ import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; import useToast from '../../../../hooks/useToast'; import PermissionButton from '../../../common/PermissionButton/PermissionButton'; import { ADMIN } from '../../../providers/AccessProvider/permissions'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; const CreateProjectRole = () => { const { setToastData, setToastApiError } = useToast(); @@ -49,8 +50,8 @@ const CreateProjectRole = () => { confetti: true, type: 'success', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; diff --git a/frontend/src/component/admin/project-roles/EditProjectRole/EditProjectRole.tsx b/frontend/src/component/admin/project-roles/EditProjectRole/EditProjectRole.tsx index eb79af949c..6b9b752bea 100644 --- a/frontend/src/component/admin/project-roles/EditProjectRole/EditProjectRole.tsx +++ b/frontend/src/component/admin/project-roles/EditProjectRole/EditProjectRole.tsx @@ -13,6 +13,7 @@ import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; import useToast from '../../../../hooks/useToast'; import PermissionButton from '../../../common/PermissionButton/PermissionButton'; import { ADMIN } from '../../../providers/AccessProvider/permissions'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; const EditProjectRole = () => { const { uiConfig } = useUiConfig(); @@ -88,8 +89,8 @@ const EditProjectRole = () => { text: 'Your role changes will automatically be applied to the users with this role.', confetti: true, }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; diff --git a/frontend/src/component/admin/project-roles/ProjectRoleForm/ProjectRoleForm.tsx b/frontend/src/component/admin/project-roles/ProjectRoleForm/ProjectRoleForm.tsx index 4b0676c471..3578deb444 100644 --- a/frontend/src/component/admin/project-roles/ProjectRoleForm/ProjectRoleForm.tsx +++ b/frontend/src/component/admin/project-roles/ProjectRoleForm/ProjectRoleForm.tsx @@ -1,16 +1,16 @@ import Input from '../../../common/Input/Input'; import EnvironmentPermissionAccordion from './EnvironmentPermissionAccordion/EnvironmentPermissionAccordion'; import { + Button, Checkbox, FormControlLabel, TextField, - Button, } from '@material-ui/core'; import useProjectRolePermissions from '../../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; import { useStyles } from './ProjectRoleForm.styles'; import ConditionallyRender from '../../../common/ConditionallyRender'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { IPermission } from '../../../../interfaces/project'; import { ICheckedPermission, @@ -33,6 +33,7 @@ interface IProjectRoleForm { clearErrors: () => void; validateNameUniqueness?: () => void; getRoleKey: (permission: { id: number; environment?: string }) => string; + children: ReactNode; } const ProjectRoleForm: React.FC = ({ diff --git a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm.styles.ts b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm.styles.ts index a2e0213f3a..572dbc27f0 100644 --- a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm.styles.ts +++ b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm.styles.ts @@ -1,4 +1,4 @@ -import { makeStyles } from '@material-ui/styles'; +import { makeStyles } from '@material-ui/core/styles'; export const useStyles = makeStyles(theme => ({ deleteParagraph: { diff --git a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx index 91632c0be3..d29839ab83 100644 --- a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx +++ b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleList.tsx @@ -16,6 +16,7 @@ import IRole, { IProjectRole } from '../../../../../interfaces/role'; import useProjectRolesApi from '../../../../../hooks/api/actions/useProjectRolesApi/useProjectRolesApi'; import useToast from '../../../../../hooks/useToast'; import ProjectRoleDeleteConfirm from '../ProjectRoleDeleteConfirm/ProjectRoleDeleteConfirm'; +import { formatUnknownError } from '../../../../../utils/format-unknown-error'; const ROOTROLE = 'root'; @@ -44,8 +45,8 @@ const ProjectRoleList = () => { title: 'Successfully deleted role', text: 'Your role is now deleted', }); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } setDelDialog(false); setConfirmName(''); diff --git a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleListItem/ProjectRoleListItem.tsx b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleListItem/ProjectRoleListItem.tsx index fc32da759d..386363abfd 100644 --- a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleListItem/ProjectRoleListItem.tsx +++ b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoleList/ProjectRoleListItem/ProjectRoleListItem.tsx @@ -1,11 +1,12 @@ import { useStyles } from './ProjectRoleListItem.styles'; -import { TableRow, TableCell, Typography } from '@material-ui/core'; -import { Edit, Delete } from '@material-ui/icons'; +import { TableCell, TableRow, Typography } from '@material-ui/core'; +import { Delete, Edit } from '@material-ui/icons'; import { ADMIN } from '../../../../../providers/AccessProvider/permissions'; import SupervisedUserCircleIcon from '@material-ui/icons/SupervisedUserCircle'; import PermissionIconButton from '../../../../../common/PermissionIconButton/PermissionIconButton'; import { IProjectRole } from '../../../../../../interfaces/role'; import { useHistory } from 'react-router-dom'; +import React from 'react'; interface IRoleListItemProps { id: number; diff --git a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoles.styles.ts b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoles.styles.ts index 0e6eea556c..7c4ce07f86 100644 --- a/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoles.styles.ts +++ b/frontend/src/component/admin/project-roles/ProjectRoles/ProjectRoles.styles.ts @@ -1,4 +1,4 @@ -import { makeStyles } from '@material-ui/styles'; +import { makeStyles } from '@material-ui/core/styles'; export const useStyles = makeStyles(theme => ({ rolesListBody: { diff --git a/frontend/src/component/admin/users/ConfirmUserAdded/ConfirmUserEmail/ConfirmUserEmail.styles.ts b/frontend/src/component/admin/users/ConfirmUserAdded/ConfirmUserEmail/ConfirmUserEmail.styles.ts index f8448ba756..9eb137338b 100644 --- a/frontend/src/component/admin/users/ConfirmUserAdded/ConfirmUserEmail/ConfirmUserEmail.styles.ts +++ b/frontend/src/component/admin/users/ConfirmUserAdded/ConfirmUserEmail/ConfirmUserEmail.styles.ts @@ -1,4 +1,4 @@ -import { makeStyles } from '@material-ui/styles'; +import { makeStyles } from '@material-ui/core/styles'; export const useStyles = makeStyles({ iconContainer: { diff --git a/frontend/src/component/admin/users/CreateUser/CreateUser.tsx b/frontend/src/component/admin/users/CreateUser/CreateUser.tsx index f480eb7535..8c4e1ffefe 100644 --- a/frontend/src/component/admin/users/CreateUser/CreateUser.tsx +++ b/frontend/src/component/admin/users/CreateUser/CreateUser.tsx @@ -10,6 +10,7 @@ import { useState } from 'react'; import { scrollToTop } from '../../../common/util'; import PermissionButton from '../../../common/PermissionButton/PermissionButton'; import { ADMIN } from '../../../providers/AccessProvider/permissions'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; const CreateUser = () => { const { setToastApiError } = useToast(); @@ -51,8 +52,8 @@ const CreateUser = () => { setInviteLink(user.inviteLink); setShowConfirm(true); }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; diff --git a/frontend/src/component/admin/users/EditUser/EditUser.tsx b/frontend/src/component/admin/users/EditUser/EditUser.tsx index 07e212bf56..c82b818ccd 100644 --- a/frontend/src/component/admin/users/EditUser/EditUser.tsx +++ b/frontend/src/component/admin/users/EditUser/EditUser.tsx @@ -11,6 +11,7 @@ import { useEffect } from 'react'; import PermissionButton from '../../../common/PermissionButton/PermissionButton'; import { ADMIN } from '../../../providers/AccessProvider/permissions'; import { EDIT } from '../../../../constants/misc'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; const EditUser = () => { useEffect(() => { @@ -60,8 +61,8 @@ const EditUser = () => { title: 'User information updated', type: 'success', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; diff --git a/frontend/src/component/admin/users/UserAdmin.styles.ts b/frontend/src/component/admin/users/UserAdmin.styles.ts index 89d94b271d..8d85cf5c4f 100644 --- a/frontend/src/component/admin/users/UserAdmin.styles.ts +++ b/frontend/src/component/admin/users/UserAdmin.styles.ts @@ -1,4 +1,4 @@ -import { makeStyles } from '@material-ui/styles'; +import { makeStyles } from '@material-ui/core/styles'; export const useStyles = makeStyles(theme => ({ userListBody: { diff --git a/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx b/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx index bd0cb82fc5..d52b58d973 100644 --- a/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx +++ b/frontend/src/component/admin/users/UsersList/ChangePassword/ChangePassword.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import classnames from 'classnames'; -import { TextField, Typography, Avatar } from '@material-ui/core'; +import { Avatar, TextField, Typography } from '@material-ui/core'; import { trim } from '../../../../common/util'; import { modalStyles } from '../../util'; import Dialogue from '../../../../common/Dialogue/Dialogue'; @@ -12,10 +12,10 @@ import { Alert } from '@material-ui/lab'; import { IUser } from '../../../../../interfaces/user'; interface IChangePasswordProps { - showDialog: () => void; + showDialog: boolean; closeDialog: () => void; - changePassword: () => void; - user: IUser; + changePassword: (user: IUser, password: string) => Promise; + user: Partial; } const ChangePassword = ({ @@ -25,7 +25,7 @@ const ChangePassword = ({ user = {}, }: IChangePasswordProps) => { const [data, setData] = useState({}); - const [error, setError] = useState({}); + const [error, setError] = useState>({}); const [validPassword, setValidPassword] = useState(false); const commonStyles = useCommonStyles(); @@ -88,7 +88,7 @@ const ChangePassword = ({ )} > {error.general}} /> diff --git a/frontend/src/component/admin/users/UsersList/DeleteUser/DeleteUser.tsx b/frontend/src/component/admin/users/UsersList/DeleteUser/DeleteUser.tsx index 03d1e1799f..c20481362c 100644 --- a/frontend/src/component/admin/users/UsersList/DeleteUser/DeleteUser.tsx +++ b/frontend/src/component/admin/users/UsersList/DeleteUser/DeleteUser.tsx @@ -9,12 +9,12 @@ import { useCommonStyles } from '../../../../../common.styles'; import { IUser } from '../../../../../interfaces/user'; interface IDeleteUserProps { - showDialog: () => void; + showDialog: boolean; closeDialog: () => void; user: IUser; userLoading: boolean; removeUser: () => void; - userApiErrors: Object; + userApiErrors: Record; } const DeleteUser = ({ @@ -33,13 +33,13 @@ const DeleteUser = ({ open={showDialog} title="Really delete user?" onClose={closeDialog} - onClick={() => removeUser(user)} + onClick={removeUser} primaryButtonText="Delete user" secondaryButtonText="Cancel" >
      string; - openUpdateDialog: (user: IUser) => (e: SyntheticEvent) => void; openPwDialog: (user: IUser) => (e: SyntheticEvent) => void; openDelDialog: (user: IUser) => (e: SyntheticEvent) => void; locationSettings: ILocationSettings; @@ -30,7 +29,6 @@ const UserListItem = ({ renderRole, openDelDialog, openPwDialog, - openUpdateDialog, locationSettings, }: IUserListItemProps) => { const { hasAccess } = useContext(AccessContext); diff --git a/frontend/src/component/admin/users/UsersList/UsersList.tsx b/frontend/src/component/admin/users/UsersList/UsersList.tsx index ca4defe92d..9d0d6208ba 100644 --- a/frontend/src/component/admin/users/UsersList/UsersList.tsx +++ b/frontend/src/component/admin/users/UsersList/UsersList.tsx @@ -24,6 +24,7 @@ import { IUser } from '../../../../interfaces/user'; import IRole from '../../../../interfaces/role'; import useToast from '../../../../hooks/useToast'; import { useLocationSettings } from '../../../../hooks/useLocationSettings'; +import { formatUnknownError } from '../../../../utils/format-unknown-error'; const UsersList = () => { const { users, roles, refetch, loading } = useUsers(); @@ -79,8 +80,8 @@ const UsersList = () => { }); refetch(); closeDelDialog(); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; @@ -172,7 +173,7 @@ const UsersList = () => { { const history = useHistory(); @@ -44,7 +45,7 @@ export const ApplicationEdit = () => { const formatDate = (v: string) => formatDateYMD(v, locationSettings.locale); - const onDeleteApplication = async (evt: Event) => { + const onDeleteApplication = async (evt: React.SyntheticEvent) => { evt.preventDefault(); try { await deleteApplication(appName); @@ -54,8 +55,8 @@ export const ApplicationEdit = () => { type: 'success', }); history.push('/applications'); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/application/ApplicationUpdate/ApplicationUpdate.tsx b/frontend/src/component/application/ApplicationUpdate/ApplicationUpdate.tsx index a3f98978be..5885f0785c 100644 --- a/frontend/src/component/application/ApplicationUpdate/ApplicationUpdate.tsx +++ b/frontend/src/component/application/ApplicationUpdate/ApplicationUpdate.tsx @@ -1,5 +1,5 @@ import { ChangeEvent, useState } from 'react'; -import { TextField, Grid } from '@material-ui/core'; +import { Grid, TextField } from '@material-ui/core'; import { useCommonStyles } from '../../../common.styles'; import icons from '../icon-names'; import GeneralSelect from '../../common/GeneralSelect/GeneralSelect'; @@ -7,6 +7,7 @@ import useApplicationsApi from '../../../hooks/api/actions/useApplicationsApi/us import useToast from '../../../hooks/useToast'; import { IApplication } from '../../../interfaces/application'; import useApplication from '../../../hooks/api/getters/useApplication/useApplication'; +import { formatUnknownError } from '../../../utils/format-unknown-error'; interface IApplicationUpdateProps { application: IApplication; @@ -35,8 +36,8 @@ export const ApplicationUpdate = ({ application }: IApplicationUpdateProps) => { title: 'Updated Successfully', text: `${field} successfully updated`, }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/application/ApplicationView/ApplicationView.tsx b/frontend/src/component/application/ApplicationView/ApplicationView.tsx index dffc1bb422..e720fe2471 100644 --- a/frontend/src/component/application/ApplicationView/ApplicationView.tsx +++ b/frontend/src/component/application/ApplicationView/ApplicationView.tsx @@ -4,16 +4,16 @@ import { Grid, List, ListItem, - ListItemText, ListItemAvatar, + ListItemText, Typography, } from '@material-ui/core'; import { - Report, Extension, - Timeline, FlagRounded, + Report, SvgIconComponent, + Timeline, } from '@material-ui/icons'; import { CREATE_FEATURE, @@ -117,10 +117,9 @@ export const ApplicationView = () => { createUrl: `/projects/default/create-toggle?name=${name}`, name, permission: CREATE_FEATURE, - i, })} elseShow={foundListItem({ - viewUrl: getTogglePath(project, name, true), + viewUrl: getTogglePath(project, name), name, description, Icon: FlagRounded, diff --git a/frontend/src/component/common/AnimateOnMount/AnimateOnMount.tsx b/frontend/src/component/common/AnimateOnMount/AnimateOnMount.tsx index fcd183c050..3b759af889 100644 --- a/frontend/src/component/common/AnimateOnMount/AnimateOnMount.tsx +++ b/frontend/src/component/common/AnimateOnMount/AnimateOnMount.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useRef, FC } from 'react'; +import React, { useEffect, useState, useRef, FC } from 'react'; import ConditionallyRender from '../ConditionallyRender'; interface IAnimateOnMountProps { @@ -7,7 +7,7 @@ interface IAnimateOnMountProps { start: string; leave: string; container?: string; - style?: Object; + style?: React.CSSProperties; } const AnimateOnMount: FC = ({ diff --git a/frontend/src/component/common/ApiError/ApiError.tsx b/frontend/src/component/common/ApiError/ApiError.tsx index e68d410cae..3d5ae22d00 100644 --- a/frontend/src/component/common/ApiError/ApiError.tsx +++ b/frontend/src/component/common/ApiError/ApiError.tsx @@ -1,5 +1,6 @@ import { Button } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; +import React from 'react'; interface IApiErrorProps { className?: string; diff --git a/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx b/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx index 88399c9c29..b80db69b3f 100644 --- a/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx +++ b/frontend/src/component/common/ConditionallyRender/ConditionallyRender.tsx @@ -1,9 +1,10 @@ interface IConditionallyRenderProps { condition: boolean; - show: JSX.Element | RenderFunc; - elseShow?: JSX.Element | RenderFunc; + show: TargetElement; + elseShow?: TargetElement; } +type TargetElement = JSX.Element | JSX.Element[] | RenderFunc | null; type RenderFunc = () => JSX.Element; const ConditionallyRender = ({ @@ -23,8 +24,9 @@ const ConditionallyRender = ({ return result; }; - const isFunc = (param: JSX.Element | RenderFunc) => - typeof param === 'function'; + const isFunc = (param: TargetElement): boolean => { + return typeof param === 'function'; + }; if (condition) { if (isFunc(show)) { diff --git a/frontend/src/component/common/Dialogue/Dialogue.tsx b/frontend/src/component/common/Dialogue/Dialogue.tsx index 80fdacc90f..4a9570bf70 100644 --- a/frontend/src/component/common/Dialogue/Dialogue.tsx +++ b/frontend/src/component/common/Dialogue/Dialogue.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { + Button, Dialog, - DialogTitle, DialogActions, DialogContent, - Button, + DialogTitle, } from '@material-ui/core'; import ConditionallyRender from '../ConditionallyRender/ConditionallyRender'; @@ -15,15 +15,15 @@ interface IDialogue { primaryButtonText?: string; secondaryButtonText?: string; open: boolean; - onClick: (e: any) => void; - onClose: () => void; + onClick: (e: React.SyntheticEvent) => void; + onClose?: (e: React.SyntheticEvent) => void; style?: object; title: string; fullWidth?: boolean; maxWidth?: 'lg' | 'sm' | 'xs' | 'md' | 'xl'; disabledPrimaryButton?: boolean; formId?: string; - permissionButton?: React.ReactNode; + permissionButton?: JSX.Element; } const Dialogue: React.FC = ({ @@ -69,7 +69,7 @@ const Dialogue: React.FC = ({ { label: string; diff --git a/frontend/src/component/common/NoItems/NoItems.tsx b/frontend/src/component/common/NoItems/NoItems.tsx index aac3c653a1..3b9bcbf656 100644 --- a/frontend/src/component/common/NoItems/NoItems.tsx +++ b/frontend/src/component/common/NoItems/NoItems.tsx @@ -1,5 +1,6 @@ import { ReactComponent as NoItemsIcon } from '../../../assets/icons/addfiles.svg'; import { useStyles } from './NoItems.styles'; +import React from 'react'; const NoItems: React.FC = ({ children }) => { const styles = useStyles(); diff --git a/frontend/src/component/common/PaginateUI/PaginateUI.tsx b/frontend/src/component/common/PaginateUI/PaginateUI.tsx index c2636c589e..6b834353a7 100644 --- a/frontend/src/component/common/PaginateUI/PaginateUI.tsx +++ b/frontend/src/component/common/PaginateUI/PaginateUI.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import ConditionallyRender from '../ConditionallyRender'; import classnames from 'classnames'; import { useStyles } from './PaginationUI.styles'; diff --git a/frontend/src/component/common/PasswordField/PasswordField.tsx b/frontend/src/component/common/PasswordField/PasswordField.tsx index 61fa587a21..ec7a636a40 100644 --- a/frontend/src/component/common/PasswordField/PasswordField.tsx +++ b/frontend/src/component/common/PasswordField/PasswordField.tsx @@ -1,6 +1,6 @@ import { IconButton, InputAdornment, TextField } from '@material-ui/core'; import { Visibility, VisibilityOff } from '@material-ui/icons'; -import { useState } from 'react'; +import React, { useState } from 'react'; const PasswordField = ({ ...rest }) => { const [showPassword, setShowPassword] = useState(false); diff --git a/frontend/src/component/common/PermissionButton/PermissionButton.tsx b/frontend/src/component/common/PermissionButton/PermissionButton.tsx index 161a2ee05c..3e6dae15b6 100644 --- a/frontend/src/component/common/PermissionButton/PermissionButton.tsx +++ b/frontend/src/component/common/PermissionButton/PermissionButton.tsx @@ -1,6 +1,6 @@ import { Button, Tooltip } from '@material-ui/core'; import { Lock } from '@material-ui/icons'; -import { useContext } from 'react'; +import React, { useContext } from 'react'; import AccessContext from '../../../contexts/AccessContext'; import ConditionallyRender from '../ConditionallyRender'; diff --git a/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx b/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx index d987e05a94..708f5b5af5 100644 --- a/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx +++ b/frontend/src/component/common/PermissionIconButton/PermissionIconButton.tsx @@ -1,5 +1,5 @@ import { IconButton, Tooltip } from '@material-ui/core'; -import { useContext } from 'react'; +import React, { useContext } from 'react'; import AccessContext from '../../../contexts/AccessContext'; interface IPermissionIconButtonProps diff --git a/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx b/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx index 0edb2cb94f..8e3f545293 100644 --- a/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx +++ b/frontend/src/component/common/ResponsiveButton/ResponsiveButton.tsx @@ -2,6 +2,7 @@ import { useMediaQuery } from '@material-ui/core'; import ConditionallyRender from '../ConditionallyRender'; import PermissionButton from '../PermissionButton/PermissionButton'; import PermissionIconButton from '../PermissionIconButton/PermissionIconButton'; +import React from 'react'; interface IResponsiveButtonProps { Icon: React.ElementType; diff --git a/frontend/src/component/common/Splash/Splash.tsx b/frontend/src/component/common/Splash/Splash.tsx index 18990afe60..455270845f 100644 --- a/frontend/src/component/common/Splash/Splash.tsx +++ b/frontend/src/component/common/Splash/Splash.tsx @@ -1,12 +1,11 @@ -import { Fragment } from 'react'; +import React, { Fragment, useState } from 'react'; import { Button, IconButton } from '@material-ui/core'; import { useStyles } from './Splash.styles'; import { + CloseOutlined, FiberManualRecord, FiberManualRecordOutlined, - CloseOutlined, } from '@material-ui/icons'; -import { useState } from 'react'; import ConditionallyRender from '../ConditionallyRender'; import { CLOSE_SPLASH } from '../../../testIds'; diff --git a/frontend/src/component/common/TagSelect/TagSelect.tsx b/frontend/src/component/common/TagSelect/TagSelect.tsx index 469e39f15b..59a572f838 100644 --- a/frontend/src/component/common/TagSelect/TagSelect.tsx +++ b/frontend/src/component/common/TagSelect/TagSelect.tsx @@ -7,7 +7,7 @@ interface ITagSelect extends React.SelectHTMLAttributes { onChange: (val: any) => void; } -const TagSelect = ({ value, types, onChange, ...rest }: ITagSelect) => { +const TagSelect = ({ value, onChange, ...rest }: ITagSelect) => { const { tagTypes } = useTagTypes(); const options = tagTypes.map(tagType => ({ diff --git a/frontend/src/component/common/ToastRenderer/Toast/Toast.tsx b/frontend/src/component/common/ToastRenderer/Toast/Toast.tsx index 0a1d83160e..397c7252d0 100644 --- a/frontend/src/component/common/ToastRenderer/Toast/Toast.tsx +++ b/frontend/src/component/common/ToastRenderer/Toast/Toast.tsx @@ -3,12 +3,12 @@ import classnames from 'classnames'; import { useContext } from 'react'; import { IconButton } from '@material-ui/core'; import CheckMarkBadge from '../../CheckmarkBadge/CheckMarkBadge'; -import UIContext, { IToastData } from '../../../../contexts/UIContext'; +import UIContext from '../../../../contexts/UIContext'; import ConditionallyRender from '../../ConditionallyRender'; import Close from '@material-ui/icons/Close'; +import { IToast } from '../../../../interfaces/toast'; -const Toast = ({ title, text, type, confetti }: IToastData) => { - // @ts-expect-error +const Toast = ({ title, text, type, confetti }: IToast) => { const { setToast } = useContext(UIContext); const styles = useStyles(); @@ -51,7 +51,7 @@ const Toast = ({ title, text, type, confetti }: IToastData) => { }; const hide = () => { - setToast((prev: IToastData) => ({ ...prev, show: false })); + setToast((prev: IToast) => ({ ...prev, show: false })); }; return ( diff --git a/frontend/src/component/common/ToastRenderer/ToastRenderer.tsx b/frontend/src/component/common/ToastRenderer/ToastRenderer.tsx index 924c9dd4a1..5f9d21cbbf 100644 --- a/frontend/src/component/common/ToastRenderer/ToastRenderer.tsx +++ b/frontend/src/component/common/ToastRenderer/ToastRenderer.tsx @@ -1,19 +1,19 @@ import { Portal } from '@material-ui/core'; import { useContext, useEffect } from 'react'; import { useCommonStyles } from '../../../common.styles'; -import UIContext, { IToastData } from '../../../contexts/UIContext'; +import UIContext from '../../../contexts/UIContext'; import { useStyles } from './ToastRenderer.styles'; import AnimateOnMount from '../AnimateOnMount/AnimateOnMount'; import Toast from './Toast/Toast'; +import { IToast } from '../../../interfaces/toast'; const ToastRenderer = () => { - // @ts-expect-error const { toastData, setToast } = useContext(UIContext); const commonStyles = useCommonStyles(); const styles = useStyles(); const hide = () => { - setToast((prev: IToastData) => ({ ...prev, show: false })); + setToast((prev: IToast) => ({ ...prev, show: false })); }; useEffect(() => { @@ -31,7 +31,7 @@ const ToastRenderer = () => { return ( { const { hasAccess } = useContext(AccessContext); @@ -46,8 +47,8 @@ const ContextList = () => { title: 'Successfully deleted context', text: 'Your context is now deleted', }); - } catch (e) { - setToastApiError(e.toString()); + } catch (error) { + setToastApiError(formatUnknownError(error)); } setName(undefined); setShowDelDialogue(false); diff --git a/frontend/src/component/context/CreateContext/CreateContext.tsx b/frontend/src/component/context/CreateContext/CreateContext.tsx index e1faa2e9f5..14c69815f7 100644 --- a/frontend/src/component/context/CreateContext/CreateContext.tsx +++ b/frontend/src/component/context/CreateContext/CreateContext.tsx @@ -8,6 +8,7 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton'; import { CREATE_CONTEXT_FIELD } from '../../providers/AccessProvider/permissions'; import useContextsApi from '../../../hooks/api/actions/useContextsApi/useContextsApi'; import useUnleashContext from '../../../hooks/api/getters/useUnleashContext/useUnleashContext'; +import { formatUnknownError } from '../../../utils/format-unknown-error'; const CreateContext = () => { const { setToastData, setToastApiError } = useToast(); @@ -46,8 +47,8 @@ const CreateContext = () => { confetti: true, type: 'success', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; @@ -69,7 +70,7 @@ const CreateContext = () => { { useEffect(() => { @@ -67,8 +68,8 @@ const EditContext = () => { title: 'Context information updated', type: 'success', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; @@ -81,7 +82,7 @@ const EditContext = () => { { const { setToastApiError, setToastData } = useToast(); @@ -49,8 +50,8 @@ const CreateEnvironment = () => { confetti: true, }); history.push('/environments'); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; diff --git a/frontend/src/component/environments/EditEnvironment/EditEnvironment.tsx b/frontend/src/component/environments/EditEnvironment/EditEnvironment.tsx index ecfc11ec5d..88efef7748 100644 --- a/frontend/src/component/environments/EditEnvironment/EditEnvironment.tsx +++ b/frontend/src/component/environments/EditEnvironment/EditEnvironment.tsx @@ -9,6 +9,7 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton'; import { ADMIN } from '../../providers/AccessProvider/permissions'; import EnvironmentForm from '../EnvironmentForm/EnvironmentForm'; import useEnvironmentForm from '../hooks/useEnvironmentForm'; +import { formatUnknownError } from '../../../utils/format-unknown-error'; const EditEnvironment = () => { const { uiConfig } = useUiConfig(); @@ -49,8 +50,8 @@ const EditEnvironment = () => { type: 'success', title: 'Successfully updated environment.', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; @@ -61,7 +62,7 @@ const EditEnvironment = () => { return ( ) => void; diff --git a/frontend/src/component/environments/EnvironmentList/EnvironmentDeleteConfirm/EnvironmentDeleteConfirm.tsx b/frontend/src/component/environments/EnvironmentList/EnvironmentDeleteConfirm/EnvironmentDeleteConfirm.tsx index 61196f50c4..0e1c8bd119 100644 --- a/frontend/src/component/environments/EnvironmentList/EnvironmentDeleteConfirm/EnvironmentDeleteConfirm.tsx +++ b/frontend/src/component/environments/EnvironmentList/EnvironmentDeleteConfirm/EnvironmentDeleteConfirm.tsx @@ -11,7 +11,7 @@ interface IEnviromentDeleteConfirmProps { open: boolean; setSelectedEnv: React.Dispatch>; setDeldialogue: React.Dispatch>; - handleDeleteEnvironment: (name: string) => Promise; + handleDeleteEnvironment: () => Promise; confirmName: string; setConfirmName: React.Dispatch>; } diff --git a/frontend/src/component/environments/EnvironmentList/EnvironmentList.tsx b/frontend/src/component/environments/EnvironmentList/EnvironmentList.tsx index 8041053b15..44136ed7ee 100644 --- a/frontend/src/component/environments/EnvironmentList/EnvironmentList.tsx +++ b/frontend/src/component/environments/EnvironmentList/EnvironmentList.tsx @@ -21,6 +21,7 @@ import EnvironmentToggleConfirm from './EnvironmentToggleConfirm/EnvironmentTogg import useProjectRolePermissions from '../../../hooks/api/getters/useProjectRolePermissions/useProjectRolePermissions'; import { ADMIN } from 'component/providers/AccessProvider/permissions'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { formatUnknownError } from '../../../utils/format-unknown-error'; const EnvironmentList = () => { const defaultEnv = { @@ -75,16 +76,16 @@ const EnvironmentList = () => { try { await sortOrderAPICall(sortOrder); refetch(); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; const sortOrderAPICall = async (sortOrder: ISortOrderPayload) => { try { await changeSortOrder(sortOrder); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; @@ -97,8 +98,8 @@ const EnvironmentList = () => { title: 'Project environment deleted', text: 'You have successfully deleted the project environment.', }); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } finally { setDeldialogue(false); setSelectedEnv(defaultEnv); @@ -124,8 +125,8 @@ const EnvironmentList = () => { title: 'Project environment enabled', text: 'Your environment is enabled', }); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } finally { refetch(); } @@ -140,8 +141,8 @@ const EnvironmentList = () => { title: 'Project environment disabled', text: 'Your environment is disabled.', }); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } finally { refetch(); } diff --git a/frontend/src/component/feature/CreateFeature/CreateFeature.tsx b/frontend/src/component/feature/CreateFeature/CreateFeature.tsx index 2fe8477254..7872f0bc90 100644 --- a/frontend/src/component/feature/CreateFeature/CreateFeature.tsx +++ b/frontend/src/component/feature/CreateFeature/CreateFeature.tsx @@ -10,6 +10,7 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton'; import { CF_CREATE_BTN_ID } from '../../../testIds'; import { useContext } from 'react'; import UIContext from '../../../contexts/UIContext'; +import { formatUnknownError } from '../../../utils/format-unknown-error'; const CreateFeature = () => { const { setToastData, setToastApiError } = useToast(); @@ -53,8 +54,8 @@ const CreateFeature = () => { type: 'success', }); setShowFeedback(true); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } } }; diff --git a/frontend/src/component/feature/EditFeature/EditFeature.tsx b/frontend/src/component/feature/EditFeature/EditFeature.tsx index 239dac8030..d23fb042d2 100644 --- a/frontend/src/component/feature/EditFeature/EditFeature.tsx +++ b/frontend/src/component/feature/EditFeature/EditFeature.tsx @@ -10,6 +10,7 @@ import { IFeatureViewParams } from '../../../interfaces/params'; import * as jsonpatch from 'fast-json-patch'; import PermissionButton from '../../common/PermissionButton/PermissionButton'; import { UPDATE_FEATURE } from '../../providers/AccessProvider/permissions'; +import { formatUnknownError } from '../../../utils/format-unknown-error'; const EditFeature = () => { const { setToastData, setToastApiError } = useToast(); @@ -57,8 +58,8 @@ const EditFeature = () => { title: 'Toggle updated successfully', type: 'success', }); - } catch (e: any) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx index 76506419c0..39ffefbd8c 100644 --- a/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx +++ b/frontend/src/component/feature/FeatureToggleListNew/FeatureToggleListNewItem/FeatureToggleListNewItem.tsx @@ -1,10 +1,12 @@ import { useRef, useState } from 'react'; import { TableCell, TableRow } from '@material-ui/core'; import { useHistory } from 'react-router'; - import { useStyles } from '../FeatureToggleListNew.styles'; import useToggleFeatureByEnv from '../../../../hooks/api/actions/useToggleFeatureByEnv/useToggleFeatureByEnv'; -import { IEnvironments } from '../../../../interfaces/featureToggle'; +import { + IEnvironments, + IFeatureEnvironment, +} from '../../../../interfaces/featureToggle'; import useToast from '../../../../hooks/useToast'; import { getTogglePath } from '../../../../utils/route-path-helpers'; import { SyntheticEvent } from 'react-router/node_modules/@types/react'; @@ -25,8 +27,8 @@ interface IFeatureToggleListNewItemProps { type: string; environments: IFeatureEnvironment[]; projectId: string; - lastSeenAt?: Date; - createdAt: Date; + lastSeenAt?: string; + createdAt: string; } const FeatureToggleListNewItem = ({ diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/AddTagDialog/AddTagDialog.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/AddTagDialog/AddTagDialog.tsx index 002f25c8af..052ea5fb3a 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/AddTagDialog/AddTagDialog.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/AddTagDialog/AddTagDialog.tsx @@ -1,6 +1,6 @@ import { DialogContentText } from '@material-ui/core'; import { useParams } from 'react-router'; -import { useState } from 'react'; +import React, { useState } from 'react'; import { IFeatureViewParams } from '../../../../../interfaces/params'; import Dialogue from '../../../../common/Dialogue'; import Input from '../../../../common/Input/Input'; @@ -11,6 +11,7 @@ import TagSelect from '../../../../common/TagSelect/TagSelect'; import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; import useTags from '../../../../../hooks/api/getters/useTags/useTags'; import useToast from '../../../../../hooks/useToast'; +import { formatUnknownError } from '../../../../../utils/format-unknown-error'; interface IAddTagDialogProps { open: boolean; @@ -20,6 +21,7 @@ interface IAddTagDialogProps { interface IDefaultTag { type: string; value: string; + [index: string]: string; } @@ -62,9 +64,10 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => { text: 'We successfully added a tag to your toggle', confetti: true, }); - } catch (e) { - setToastApiError(e.message); - setErrors({ tagError: e.message }); + } catch (error: unknown) { + const message = formatUnknownError(error); + setToastApiError(message); + setErrors({ tagError: message }); } }; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx index 96d6a68999..819900aae3 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvSwitches/FeatureOverviewEnvSwitch/FeatureOverviewEnvSwitch.tsx @@ -8,6 +8,8 @@ import { IFeatureViewParams } from '../../../../../../interfaces/params'; import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch'; import StringTruncator from '../../../../../common/StringTruncator/StringTruncator'; import { UPDATE_FEATURE_ENVIRONMENT } from '../../../../../providers/AccessProvider/permissions'; +import React from 'react'; +import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; interface IFeatureOverviewEnvSwitchProps { env: IFeatureEnvironment; @@ -40,7 +42,7 @@ const FeatureOverviewEnvSwitch = ({ if (callback) { callback(); } - } catch (e: any) { + } catch (e) { if (e.message === ENVIRONMENT_STRATEGY_ERROR) { showInfoBox(true); } else { @@ -61,8 +63,8 @@ const FeatureOverviewEnvSwitch = ({ if (callback) { callback(); } - } catch (e: any) { - setToastApiError(e.message); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx index beda59ba22..1f4e852a24 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx @@ -1,5 +1,5 @@ import { Settings } from '@material-ui/icons'; -import { useTheme } from '@material-ui/styles'; +import { useTheme } from '@material-ui/core/styles'; import { Link, useParams } from 'react-router-dom'; import { IFeatureViewParams } from '../../../../../../../../interfaces/params'; import { IFeatureStrategy } from '../../../../../../../../interfaces/strategy'; diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewTags/FeatureOverviewTags.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewTags/FeatureOverviewTags.tsx index 33f484fd51..7b3570e72d 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewTags/FeatureOverviewTags.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewMetaData/FeatureOverviewTags/FeatureOverviewTags.tsx @@ -1,6 +1,6 @@ -import { useState, useContext } from 'react'; +import React, { useContext, useState } from 'react'; import { Chip } from '@material-ui/core'; -import { Label, Close } from '@material-ui/icons'; +import { Close, Label } from '@material-ui/icons'; import { useParams } from 'react-router-dom'; import useTags from '../../../../../../hooks/api/getters/useTags/useTags'; import { IFeatureViewParams } from '../../../../../../interfaces/params'; @@ -17,6 +17,7 @@ import useToast from '../../../../../../hooks/useToast'; import { UPDATE_FEATURE } from '../../../../../providers/AccessProvider/permissions'; import ConditionallyRender from '../../../../../common/ConditionallyRender'; import AccessContext from '../../../../../../contexts/AccessContext'; +import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; interface IFeatureOverviewTagsProps extends React.HTMLProps { projectId: string; @@ -53,8 +54,8 @@ const FeatureOverviewTags: React.FC = ({ title: 'Tag deleted', text: 'Successfully deleted tag', }); - } catch (e) { - setToastApiError(e.message); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsMetadata/FeatureSettingsMetadata.tsx b/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsMetadata/FeatureSettingsMetadata.tsx index 0c7bd6e577..204431e32e 100644 --- a/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsMetadata/FeatureSettingsMetadata.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsMetadata/FeatureSettingsMetadata.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; import * as jsonpatch from 'fast-json-patch'; import { TextField } from '@material-ui/core'; import PermissionButton from '../../../../common/PermissionButton/PermissionButton'; @@ -11,6 +11,7 @@ import { IFeatureViewParams } from '../../../../../interfaces/params'; import useToast from '../../../../../hooks/useToast'; import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; import ConditionallyRender from '../../../../common/ConditionallyRender'; +import { formatUnknownError } from '../../../../../utils/format-unknown-error'; const FeatureSettingsMetadata = () => { const { hasAccess } = useContext(AccessContext); @@ -54,8 +55,8 @@ const FeatureSettingsMetadata = () => { }); setDirty(false); refetch(); - } catch (e) { - setToastApiError(e.toString()); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsProject/FeatureSettingsProject.tsx b/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsProject/FeatureSettingsProject.tsx index b33c203f1c..663e627289 100644 --- a/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsProject/FeatureSettingsProject.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureSettings/FeatureSettingsProject/FeatureSettingsProject.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { useHistory, useParams } from 'react-router'; import AccessContext from '../../../../../contexts/AccessContext'; import useFeatureApi from '../../../../../hooks/api/actions/useFeatureApi/useFeatureApi'; @@ -12,6 +12,7 @@ import FeatureProjectSelect from './FeatureProjectSelect/FeatureProjectSelect'; import FeatureSettingsProjectConfirm from './FeatureSettingsProjectConfirm/FeatureSettingsProjectConfirm'; import { IPermission } from '../../../../../interfaces/user'; import { useAuthPermissions } from '../../../../../hooks/api/getters/useAuth/useAuthPermissions'; +import { formatUnknownError } from '../../../../../utils/format-unknown-error'; const FeatureSettingsProject = () => { const { hasAccess } = useContext(AccessContext); @@ -61,16 +62,16 @@ const FeatureSettingsProject = () => { history.replace( `/projects/${newProject}/features/${featureId}/settings` ); - } catch (e) { - setToastApiError(e.message); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; const createMoveTargets = () => { return permissions.reduce( - (acc: { [key: string]: boolean }, permission: IPermission) => { - if (permission.permission === MOVE_FEATURE_TOGGLE) { - acc[permission.project] = true; + (acc: { [key: string]: boolean }, p: IPermission) => { + if (p.project && p.permission === MOVE_FEATURE_TOGGLE) { + acc[p.project] = true; } return acc; }, diff --git a/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx b/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx index b7b25230c9..7fa2d08228 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStatus/FeatureStatus.tsx @@ -1,7 +1,8 @@ import { useStyles } from './FeatureStatus.styles'; import TimeAgo from 'react-timeago'; import ConditionallyRender from '../../../common/ConditionallyRender'; -import { Tooltip } from '@material-ui/core'; +import { Tooltip, TooltipProps } from '@material-ui/core'; +import React from 'react'; function generateUnit(unit?: string): string { switch (unit) { @@ -46,8 +47,8 @@ function getColor(unit?: string): string { } interface FeatureStatusProps { - lastSeenAt?: Date; - tooltipPlacement?: string; + lastSeenAt?: string; + tooltipPlacement?: TooltipProps['placement']; } const FeatureStatus = ({ @@ -76,7 +77,7 @@ const FeatureStatus = ({ condition={!!lastSeenAt} show={ { const history = useHistory(); @@ -99,8 +100,8 @@ const FeatureStrategiesConfigure = () => { history.replace(history.location.pathname); refetch(); scrollToTop(); - } catch (e) { - setToastApiError(e.message); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/FeatureStrategiesEnvironmentList.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/FeatureStrategiesEnvironmentList.tsx index f7083ddad1..5b03e163a5 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/FeatureStrategiesEnvironmentList.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/FeatureStrategiesEnvironmentList.tsx @@ -39,7 +39,6 @@ const FeatureStrategiesEnvironmentList = ({ const { activeEnvironmentsRef, - setToastData, deleteStrategy, updateStrategy, delDialog, @@ -162,7 +161,6 @@ const FeatureStrategiesEnvironmentList = ({ : 'Toggle is disabled and no strategies are executing' } env={activeEnvironment} - setToastData={setToastData} callback={updateFeatureEnvironmentCache} />
      diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/useFeatureStrategiesEnvironmentList.ts b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/useFeatureStrategiesEnvironmentList.ts index 5d21ef1536..6a7ee41dd4 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/useFeatureStrategiesEnvironmentList.ts +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesEnvironmentList/useFeatureStrategiesEnvironmentList.ts @@ -4,9 +4,13 @@ import FeatureStrategiesUIContext from '../../../../../../contexts/FeatureStrate import useFeatureStrategyApi from '../../../../../../hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; import useToast from '../../../../../../hooks/useToast'; import { IFeatureViewParams } from '../../../../../../interfaces/params'; -import { IFeatureStrategy } from '../../../../../../interfaces/strategy'; +import { + IFeatureStrategy, + IStrategyPayload, +} from '../../../../../../interfaces/strategy'; import cloneDeep from 'lodash.clonedeep'; import { IFeatureEnvironment } from '../../../../../../interfaces/featureToggle'; +import { formatUnknownError } from '../../../../../../utils/format-unknown-error'; const useFeatureStrategiesEnvironmentList = () => { const { projectId, featureId } = useParams(); @@ -85,8 +89,8 @@ const useFeatureStrategiesEnvironmentList = () => { strategy.constraints = updateStrategyPayload.constraints; history.replace(history.location.pathname); setFeatureCache(feature); - } catch (e) { - setToastApiError(e.message); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; @@ -118,14 +122,13 @@ const useFeatureStrategiesEnvironmentList = () => { text: `Successfully deleted strategy from ${featureId}`, }); history.replace(history.location.pathname); - } catch (e) { - setToastApiError(e.message); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); } }; return { activeEnvironmentsRef, - setToastData, deleteStrategy, updateStrategy, delDialog, diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx index fa50fe9fab..c64362a211 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategiesProductionGuard/FeatureStrategiesProductionGuard.tsx @@ -11,7 +11,7 @@ interface IFeatureStrategiesProductionGuard { onClick: () => void; onClose: () => void; primaryButtonText: string; - loading: boolean; + loading?: boolean; } const FeatureStrategiesProductionGuard = ({ diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx index 0c8afb086c..89597a3a24 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import { mutate } from 'swr'; import FeatureStrategiesUIContext from '../../../../../../contexts/FeatureStrategiesUIContext'; @@ -6,8 +6,8 @@ import useFeatureStrategy from '../../../../../../hooks/api/getters/useFeatureSt import { IFeatureViewParams } from '../../../../../../interfaces/params'; import { IConstraint, - IParameter, IFeatureStrategy, + IParameter, } from '../../../../../../interfaces/strategy'; import FeatureStrategyAccordion from '../../FeatureStrategyAccordion/FeatureStrategyAccordion'; import cloneDeep from 'lodash.clonedeep'; diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesList/FeatureStrategyCard/FeatureStrategyCard.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesList/FeatureStrategyCard/FeatureStrategyCard.tsx index 4dd9b6f82d..91e69f83bb 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesList/FeatureStrategyCard/FeatureStrategyCard.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesList/FeatureStrategyCard/FeatureStrategyCard.tsx @@ -23,7 +23,7 @@ interface IFeatureStrategyCardProps { name: string; description: string; configureNewStrategy: boolean; - index?: number; + index: number; } export const FEATURE_STRATEGIES_DRAG_TYPE = 'FEATURE_STRATEGIES_DRAG_TYPE'; diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordionBody/FeatureStrategyAccordionBody.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordionBody/FeatureStrategyAccordionBody.tsx index 942772a9cc..14a73594a3 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordionBody/FeatureStrategyAccordionBody.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategyAccordion/FeatureStrategyAccordionBody/FeatureStrategyAccordionBody.tsx @@ -8,7 +8,7 @@ import useStrategies from '../../../../../../hooks/api/getters/useStrategies/use import GeneralStrategy from '../../common/GeneralStrategy/GeneralStrategy'; import UserWithIdStrategy from '../../common/UserWithIdStrategy/UserWithId'; import StrategyConstraints from '../../common/StrategyConstraints/StrategyConstraints'; -import { useContext, useState } from 'react'; +import React, { useContext, useState } from 'react'; import ConditionallyRender from '../../../../../common/ConditionallyRender'; import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiConfig'; import { C } from '../../../../../common/flags'; diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/GeneralStrategy/GeneralStrategy.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/GeneralStrategy/GeneralStrategy.tsx index 131bf391d8..94c16f9d0e 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/GeneralStrategy/GeneralStrategy.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/GeneralStrategy/GeneralStrategy.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { - Switch, FormControlLabel, - Tooltip, + Switch, TextField, + Tooltip, } from '@material-ui/core'; import StrategyInputList from '../StrategyInputList/StrategyInputList'; import RolloutSlider from '../RolloutSlider/RolloutSlider'; import { - IParameter, IFeatureStrategy, + IParameter, } from '../../../../../../interfaces/strategy'; import { useStyles } from './GeneralStrategy.styles'; @@ -77,7 +77,7 @@ const GeneralStrategy = ({
      ); } else if (type === 'list') { - let list = []; + let list: string[] = []; if (typeof value === 'string') { list = value.trim().split(',').filter(Boolean); } diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/RolloutSlider/RolloutSlider.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/RolloutSlider/RolloutSlider.tsx index 0ecace92ba..0fad306563 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/RolloutSlider/RolloutSlider.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/RolloutSlider/RolloutSlider.tsx @@ -1,6 +1,7 @@ import { makeStyles, withStyles } from '@material-ui/core/styles'; import { Slider, Typography } from '@material-ui/core'; import { ROLLOUT_SLIDER_ID } from '../../../../../../testIds'; +import React from 'react'; const StyledSlider = withStyles({ root: { diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyConstraints/StrategyConstraints.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyConstraints/StrategyConstraints.tsx index cf10e8f777..9e13cc8f6a 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyConstraints/StrategyConstraints.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyConstraints/StrategyConstraints.tsx @@ -7,7 +7,7 @@ import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiCo import { C } from '../../../../../common/flags'; import useUnleashContext from '../../../../../../hooks/api/getters/useUnleashContext/useUnleashContext'; import StrategyConstraintInputField from './StrategyConstraintInputField'; -import { useEffect } from 'react'; +import React, { useEffect } from 'react'; interface IStrategyConstraintProps { constraints: IConstraint[]; @@ -38,7 +38,7 @@ const StrategyConstraints: React.FC = ({ const enabled = uiConfig.flags[C]; const contextNames = contextFields.map(context => context.name); - const onClick = evt => { + const onClick = (evt: React.SyntheticEvent) => { evt.preventDefault(); addConstraint(); }; @@ -57,15 +57,15 @@ const StrategyConstraints: React.FC = ({ }; }; - const removeConstraint = index => evt => { - evt.preventDefault(); + const removeConstraint = (index: number) => (event: Event) => { + event.preventDefault(); const updatedConstraints = [...constraints]; updatedConstraints.splice(index, 1); updateConstraints(updatedConstraints); }; - const updateConstraint = index => (value, field) => { + const updateConstraint = (index: number) => (value, field) => { const updatedConstraints = [...constraints]; const constraint = updatedConstraints[index]; constraint[field] = value; diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyInputList/StrategyInputList.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyInputList/StrategyInputList.tsx index cdd857aa16..47cde8175d 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyInputList/StrategyInputList.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/StrategyInputList/StrategyInputList.tsx @@ -10,7 +10,7 @@ import { interface IStrategyInputList { name: string; list: string[]; - setConfig: () => void; + setConfig: (field: string, value: string) => void; disabled: boolean; } diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/UserWithIdStrategy/UserWithId.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/UserWithIdStrategy/UserWithId.tsx index ee9b7360fb..4d605d2791 100644 --- a/frontend/src/component/feature/FeatureView/FeatureStrategies/common/UserWithIdStrategy/UserWithId.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/common/UserWithIdStrategy/UserWithId.tsx @@ -3,7 +3,7 @@ import StrategyInputList from '../StrategyInputList/StrategyInputList'; interface IUserWithIdStrategyProps { parameters: IParameter; - updateParameter: (field: string, value: any) => void; + updateParameter: (field: string, value: string) => void; editable: boolean; } diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx index 60ffa01987..cde71a704d 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/AddFeatureVariant.tsx @@ -1,12 +1,11 @@ -import { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; +import React, { useEffect, useState } from 'react'; import { + Button, FormControl, FormControlLabel, Grid, - TextField, InputAdornment, - Button, + TextField, Tooltip, } from '@material-ui/core'; import { Info } from '@material-ui/icons'; @@ -18,13 +17,16 @@ import ConditionallyRender from '../../../../../common/ConditionallyRender'; import GeneralSelect from '../../../../../common/GeneralSelect/GeneralSelect'; import { useCommonStyles } from '../../../../../../common.styles'; import Dialogue from '../../../../../common/Dialogue'; -import { trim, modalStyles } from '../../../../../common/util'; +import { modalStyles, trim } from '../../../../../common/util'; import PermissionSwitch from '../../../../../common/PermissionSwitch/PermissionSwitch'; import { UPDATE_FEATURE_VARIANTS } from '../../../../../providers/AccessProvider/permissions'; import useFeature from '../../../../../../hooks/api/getters/useFeature/useFeature'; import { useParams } from 'react-router-dom'; import { IFeatureViewParams } from '../../../../../../interfaces/params'; -import { IFeatureVariant } from '../../../../../../interfaces/featureToggle'; +import { + IFeatureVariant, + IOverride, +} from '../../../../../../interfaces/featureToggle'; import cloneDeep from 'lodash.clonedeep'; const payloadOptions = [ @@ -35,6 +37,17 @@ const payloadOptions = [ const EMPTY_PAYLOAD = { type: 'string', value: '' }; +interface IAddVariantProps { + showDialog: boolean; + closeDialog: () => void; + save: (variantToSave: IFeatureVariant) => Promise; + editVariant: IFeatureVariant; + validateName: (value: string) => Record | undefined; + validateWeight: (value: string) => Record | undefined; + title: string; + editing: boolean; +} + const AddVariant = ({ showDialog, closeDialog, @@ -44,11 +57,11 @@ const AddVariant = ({ validateWeight, title, editing, -}) => { +}: IAddVariantProps) => { const [data, setData] = useState({}); const [payload, setPayload] = useState(EMPTY_PAYLOAD); - const [overrides, setOverrides] = useState([]); - const [error, setError] = useState({}); + const [overrides, setOverrides] = useState([]); + const [error, setError] = useState>({}); const commonStyles = useCommonStyles(); const { projectId, featureId } = useParams(); const { feature } = useFeature(projectId, featureId); @@ -80,7 +93,7 @@ const AddVariant = ({ setError({}); }; - const setClonedVariants = clonedVariants => + const setClonedVariants = (clonedVariants: IFeatureVariant[]) => setVariants(cloneDeep(clonedVariants)); useEffect(() => { @@ -159,7 +172,7 @@ const AddVariant = ({ } }; - const onPayload = e => { + const onPayload = (e: React.SyntheticEvent) => { e.preventDefault(); setPayload({ ...payload, @@ -167,13 +180,13 @@ const AddVariant = ({ }); }; - const onCancel = e => { + const onCancel = (e: React.SyntheticEvent) => { e.preventDefault(); clear(); closeDialog(); }; - const updateOverrideType = index => e => { + const updateOverrideType = (index: number) => (e: React.SyntheticEvent) => { e.preventDefault(); setOverrides( overrides.map((o, i) => { @@ -186,7 +199,7 @@ const AddVariant = ({ ); }; - const updateOverrideValues = (index, values) => { + const updateOverrideValues = (index: number, values: string[]) => { setOverrides( overrides.map((o, i) => { if (i === index) { @@ -197,12 +210,12 @@ const AddVariant = ({ ); }; - const removeOverride = index => e => { + const removeOverride = (index: number) => (e: React.SyntheticEvent) => { e.preventDefault(); setOverrides(overrides.filter((o, i) => i !== index)); }; - const onAddOverride = e => { + const onAddOverride = (e: React.SyntheticEvent) => { e.preventDefault(); setOverrides([ ...overrides, @@ -388,7 +401,6 @@ const AddVariant = ({ removeOverride={removeOverride} updateOverrideType={updateOverrideType} updateOverrideValues={updateOverrideValues} - updateValues={updateOverrideValues} />