diff --git a/frontend/cypress/integration/feature-toggle/feature.spec.js b/frontend/cypress/integration/feature-toggle/feature.spec.js index 4beadf4e5a..487016af0f 100644 --- a/frontend/cypress/integration/feature-toggle/feature.spec.js +++ b/frontend/cypress/integration/feature-toggle/feature.spec.js @@ -96,7 +96,7 @@ describe('feature toggle', () => { it('Can add a gradual rollout strategy to the development environment', () => { cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/strategies`); + 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(); cy.get('[data-test=ROLLOUT_SLIDER_ID') @@ -139,7 +139,7 @@ describe('feature toggle', () => { it('can update a strategy in the development environment', () => { cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/strategies`); + cy.visit(`/projects/default/features/${featureToggleName}/strategies`); cy.get('[data-test=STRATEGY_ACCORDION_ID-flexibleRollout').click(); cy.get('[data-test=ROLLOUT_SLIDER_ID') @@ -186,7 +186,7 @@ describe('feature toggle', () => { it('can delete a strategy in the development environment', () => { cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/strategies`); + cy.visit(`/projects/default/features/${featureToggleName}/strategies`); cy.intercept( 'DELETE', @@ -205,7 +205,7 @@ describe('feature toggle', () => { it('Can add a userid strategy to the development environment', () => { cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/strategies`); + 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(); @@ -252,7 +252,7 @@ describe('feature toggle', () => { const variantName = 'my-new-variant'; const secondVariantName = 'my-second-variant'; cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/variants`); + cy.visit(`/projects/default/features/${featureToggleName}/variants`); cy.intercept( 'PATCH', `/api/admin/projects/default/features/${featureToggleName}/variants`, @@ -283,7 +283,7 @@ describe('feature toggle', () => { it('Can set weight to fixed value for one of the variants', () => { cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/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() @@ -319,7 +319,7 @@ describe('feature toggle', () => { it(`can delete variant`, () => { const variantName = 'to-be-deleted'; cy.wait(500); - cy.visit(`/projects/default/features2/${featureToggleName}/variants`); + 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=DIALOGUE_CONFIRM_ID]').click(); diff --git a/frontend/package.json b/frontend/package.json index 147ef56a23..4ae3bffda7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "unleash-frontend", "description": "unleash your features", - "version": "4.7.0", + "version": "4.7.1", "keywords": [ "unleash", "feature toggle", @@ -45,12 +45,11 @@ "@testing-library/user-event": "13.5.0", "@types/debounce": "1.2.1", "@types/deep-diff": "1.0.1", - "@types/enzyme": "3.10.11", - "@types/enzyme-adapter-react-16": "1.0.6", "@types/jest": "27.4.0", "@types/node": "14.18.10", "@types/react": "17.0.39", "@types/react-dom": "17.0.11", + "@types/react-outside-click-handler": "^1.3.1", "@types/react-router-dom": "5.3.3", "@types/react-test-renderer": "17.0.1", "@types/react-timeago": "4.1.3", @@ -64,9 +63,6 @@ "date-fns": "2.28.0", "debounce": "1.2.1", "deep-diff": "1.0.2", - "enzyme": "3.11.0", - "enzyme-adapter-react-16": "1.15.6", - "enzyme-to-json": "3.6.2", "fast-json-patch": "3.1.0", "fetch-mock": "9.11.0", "http-proxy-middleware": "2.0.2", @@ -75,6 +71,7 @@ "lodash.flow": "3.5.0", "node-fetch": "2.6.7", "prettier": "2.5.1", + "prop-types": "^15.8.1", "react": "17.0.2", "react-dnd": "14.0.5", "react-dnd-html5-backend": "14.1.0", @@ -83,6 +80,7 @@ "react-redux": "7.2.6", "react-router-dom": "5.3.0", "react-scripts": "4.0.3", + "react-test-renderer": "^16.14.0", "react-timeago": "6.2.1", "redux": "4.1.2", "redux-devtools-extension": "2.13.9", @@ -95,12 +93,10 @@ }, "jest": { "moduleNameMapper": { - "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.js", + "\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/src/__mocks__/fileMock.js", + "\\.svg": "/src/__mocks__/svgMock.js", "\\.(css|scss)$": "identity-obj-proxy" - }, - "snapshotSerializers": [ - "enzyme-to-json/serializer" - ] + } }, "browserslist": { "production": [ diff --git a/frontend/src/__mocks__/svgMock.js b/frontend/src/__mocks__/svgMock.js new file mode 100644 index 0000000000..13afca51d6 --- /dev/null +++ b/frontend/src/__mocks__/svgMock.js @@ -0,0 +1,2 @@ +export default 'SvgrURL' +export const ReactComponent = 'div' diff --git a/frontend/src/component/Reporting/ReportCard/ReportCard.jsx b/frontend/src/component/Reporting/ReportCard/ReportCard.tsx similarity index 78% rename from frontend/src/component/Reporting/ReportCard/ReportCard.jsx rename to frontend/src/component/Reporting/ReportCard/ReportCard.tsx index 26c3370c17..88a446efb3 100644 --- a/frontend/src/component/Reporting/ReportCard/ReportCard.jsx +++ b/frontend/src/component/Reporting/ReportCard/ReportCard.tsx @@ -1,23 +1,19 @@ import classnames from 'classnames'; import { Paper } from '@material-ui/core'; -import PropTypes from 'prop-types'; - import CheckIcon from '@material-ui/icons/Check'; import ReportProblemOutlinedIcon from '@material-ui/icons/ReportProblemOutlined'; import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; - import styles from './ReportCard.module.scss'; import ReactTimeAgo from 'react-timeago'; +import { IProjectHealthReport } from "../../../interfaces/project"; -const ReportCard = ({ - health, - activeCount, - staleCount, - potentiallyStaleCount, - lastUpdate, -}) => { - const healthLessThan50 = health < 50; - const healthLessThan75 = health < 75; +interface IReportCardProps { + healthReport: IProjectHealthReport +} + +export const ReportCard = ({ healthReport }: IReportCardProps) => { + const healthLessThan50 = healthReport.health < 50; + const healthLessThan75 = healthReport.health < 75; const healthClasses = classnames(styles.reportCardHealthRating, { [styles.healthWarning]: healthLessThan75, @@ -27,21 +23,21 @@ const ReportCard = ({ const renderActiveToggles = () => ( <> - {activeCount} active toggles + {healthReport.activeCount} active toggles ); const renderStaleToggles = () => ( <> - {staleCount} stale toggles + {healthReport.staleCount} stale toggles ); const renderPotentiallyStaleToggles = () => ( <> - {potentiallyStaleCount} potentially stale toggles + {healthReport.potentiallyStaleCount} potentially stale toggles ); @@ -52,14 +48,14 @@ const ReportCard = ({

Health rating

-1} + condition={healthReport.health > -1} show={
-

{health}%

+

{healthReport.health}%

Last updated:{' '}

@@ -73,12 +69,12 @@ const ReportCard = ({
{ ); }; -export default FeatureView2; +export default FeatureView; diff --git a/frontend/src/component/feature/FeatureView/index.jsx b/frontend/src/component/feature/FeatureView/index.jsx deleted file mode 100644 index 880a68ad79..0000000000 --- a/frontend/src/component/feature/FeatureView/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import { connect } from 'react-redux'; - -import { - fetchFeatureToggles, - fetchFeatureToggle, - toggleFeature, - setStale, - removeFeatureToggle, - editFeatureToggle, -} from '../../../store/feature-toggle/actions'; - -import FeatureView from './FeatureView'; -import { - fetchTags, - tagFeature, - untagFeature, -} from '../../../store/feature-tags/actions'; - -export default connect( - (state, props) => ({ - features: state.features.toJS(), - featureToggle: state.features - .toJS() - .find(toggle => toggle.name === props.featureToggleName), - featureTags: state.featureTags.toJS(), - tagTypes: state.tagTypes.toJS(), - activeTab: props.activeTab, - user: state.user.toJS(), - }), - { - fetchFeatureToggles, - fetchFeatureToggle, - toggleFeature, - setStale, - removeFeatureToggle, - editFeatureToggle, - tagFeature, - untagFeature, - fetchTags, - } -)(FeatureView); diff --git a/frontend/src/component/feature/ProgressWheel.jsx b/frontend/src/component/feature/ProgressWheel.jsx deleted file mode 100644 index 206e33a772..0000000000 --- a/frontend/src/component/feature/ProgressWheel.jsx +++ /dev/null @@ -1,153 +0,0 @@ -import { useState, useEffect, useRef } from 'react'; -import PropTypes from 'prop-types'; -import styles from './progress.module.scss'; - -const Progress = ({ - percentage, - strokeWidth = 10, - initialAnimation = false, - animatePercentageText = false, - textForPercentage, - colorClassName, - isFallback = false, -}) => { - const [localPercentage, setLocalPercentage] = useState({ - percentage: initialAnimation ? 0 : percentage, - percentageText: initialAnimation ? 0 : percentage, - }); - const timeoutId = useRef(); - const rafTimerInit = useRef(); - const rafCounterTimer = useRef(); - const nextTimer = useRef(); - - useEffect(() => { - if (initialAnimation) { - timeoutId.current = setTimeout(() => { - rafTimerInit.current = window.requestAnimationFrame(() => { - setLocalPercentage(prev => ({ ...prev, percentage })); - }); - }, 0); - } - - return () => { - clearTimeout(timeoutId.current); - clearTimeout(nextTimer); - window.cancelAnimationFrame(rafTimerInit.current); - window.cancelAnimationFrame(rafCounterTimer.current); - }; - /* eslint-disable-next-line */ - }, []); - - useEffect(() => { - if (percentage !== localPercentage) { - const nextState = { percentage }; - if (animatePercentageText) { - animateTo(percentage, getTarget(percentage)); - } else { - nextState.percentageText = percentage; - } - setLocalPercentage(prev => ({ ...prev, ...nextState })); - } - /* eslint-disable-next-line */ - }, [percentage]); - - const getTarget = target => { - const start = localPercentage.percentageText; - const TOTAL_ANIMATION_TIME = 5000; - const diff = start > target ? -(start - target) : target - start; - const perCycle = TOTAL_ANIMATION_TIME / diff; - const cyclesCounter = Math.round( - Math.abs(TOTAL_ANIMATION_TIME / perCycle) - ); - const perCycleTime = Math.round(Math.abs(perCycle)); - - return { - start, - target, - cyclesCounter, - perCycleTime, - increment: diff / cyclesCounter, - }; - }; - - const animateTo = (percentage, targetState) => { - cancelAnimationFrame(rafCounterTimer.current); - clearTimeout(nextTimer.current); - - const current = localPercentage.percentageText; - - targetState.cyclesCounter--; - if (targetState.cyclesCounter <= 0) { - setLocalPercentage({ percentageText: targetState.target }); - return; - } - - const next = Math.round(current + targetState.increment); - rafCounterTimer.current = requestAnimationFrame(() => { - setLocalPercentage({ percentageText: next }); - nextTimer.current = setTimeout(() => { - animateTo(next, targetState); - }, targetState.perCycleTime); - }); - }; - - const radius = 50 - strokeWidth / 2; - const pathDescription = ` - M 50,50 m 0,-${radius} - a ${radius},${radius} 0 1 1 0,${2 * radius} - a ${radius},${radius} 0 1 1 0,-${2 * radius} - `; - - const diameter = Math.PI * 2 * radius; - const progressStyle = { - strokeDasharray: `${diameter}px ${diameter}px`, - strokeDashoffset: `${ - ((100 - localPercentage.percentage) / 100) * diameter - }px`, - }; - - return isFallback ? ( - - { - // eslint-disable-next-line max-len - } - - - ) : ( - - - - - - - {localPercentage.percentageText}% - - - ); -}; - -Progress.propTypes = { - percentage: PropTypes.number.isRequired, - strokeWidth: PropTypes.number, - initialAnimation: PropTypes.bool, - animatePercentageText: PropTypes.bool, - textForPercentage: PropTypes.func, - colorClassName: PropTypes.string, - isFallback: PropTypes.bool, -}; - -export default Progress; diff --git a/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx b/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx index 3d97d1ae4a..c642cf0800 100644 --- a/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx +++ b/frontend/src/component/feature/RedirectFeatureView/RedirectFeatureView.tsx @@ -1,30 +1,33 @@ -import { useEffect } from 'react'; -import { Redirect } from 'react-router-dom'; +import { useEffect, useState } from 'react'; +import { Redirect, useParams } from 'react-router-dom'; +import useFeatures from '../../../hooks/api/getters/useFeatures/useFeatures'; +import { IFeatureToggle } from '../../../interfaces/featureToggle'; import { getTogglePath } from '../../../utils/route-path-helpers'; -interface IRedirectFeatureViewProps { - featureToggle: any; - features: any; - fetchFeatureToggles: () => void; - newPath: boolean; +interface IRedirectParams { + name: string; } -const RedirectFeatureView = ({ - featureToggle, - fetchFeatureToggles, - newPath = false, -}: IRedirectFeatureViewProps) => { +const RedirectFeatureView = () => { + const { name } = useParams(); + const { features } = useFeatures(); + const [featureToggle, setFeatureToggle] = useState( + null + ); + useEffect(() => { - if (!featureToggle) { - fetchFeatureToggles(); - } - /* eslint-disable-next-line */ - }, []); + const toggle = features.find( + (toggle: IFeatureToggle) => toggle.name === name + ); + + setFeatureToggle(toggle); + }, [features, name]); if (!featureToggle) return null; + return ( ); }; diff --git a/frontend/src/component/feature/RedirectFeatureView/index.ts b/frontend/src/component/feature/RedirectFeatureView/index.ts deleted file mode 100644 index d9e2b5b595..0000000000 --- a/frontend/src/component/feature/RedirectFeatureView/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { connect } from 'react-redux'; - -import { fetchFeatureToggles } from '../../../store/feature-toggle/actions'; - -import RedirectFeatureView from './RedirectFeatureView'; - -import { E } from '../../common/flags'; - -export default connect( - (state, props) => ({ - newPath: !!state.uiConfig.toJS().flags[E], - featureToggle: state.features - .toJS() - .find(toggle => toggle.name === props.featureToggleName), - }), - { - fetchFeatureToggles, - } -)(RedirectFeatureView); diff --git a/frontend/src/component/feature/__tests__/__snapshots__/progress-test.jsx.snap b/frontend/src/component/feature/__tests__/__snapshots__/progress-test.jsx.snap deleted file mode 100644 index 4ff1c90119..0000000000 --- a/frontend/src/component/feature/__tests__/__snapshots__/progress-test.jsx.snap +++ /dev/null @@ -1,105 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly with 0% done no fallback 1`] = ` - - - - - 0 - % - - -`; - -exports[`renders correctly with 0% done with fallback 1`] = ` - - - -`; - -exports[`renders correctly with 15% done no fallback 1`] = ` - - - - - 15 - % - - -`; - -exports[`renders correctly with 15% done with fallback 1`] = ` - - - -`; diff --git a/frontend/src/component/feature/__tests__/progress-test.jsx b/frontend/src/component/feature/__tests__/progress-test.jsx deleted file mode 100644 index ce22d6e886..0000000000 --- a/frontend/src/component/feature/__tests__/progress-test.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; - -import Progress from '../ProgressWheel'; -import renderer from 'react-test-renderer'; - -jest.mock('@material-ui/core'); - -test('renders correctly with 15% done no fallback', () => { - const percent = 15; - const tree = renderer.create( - - ); - - expect(tree).toMatchSnapshot(); -}); - -test('renders correctly with 0% done no fallback', () => { - const percent = 0; - const tree = renderer.create( - - ); - - expect(tree).toMatchSnapshot(); -}); - -test('renders correctly with 15% done with fallback', () => { - const percent = 15; - const tree = renderer.create( - - ); - - expect(tree).toMatchSnapshot(); -}); - -test('renders correctly with 0% done with fallback', () => { - const percent = 0; - const tree = renderer.create( - - ); - - expect(tree).toMatchSnapshot(); -}); diff --git a/frontend/src/component/feature/add-tag-dialog-component.jsx b/frontend/src/component/feature/add-tag-dialog-component.jsx deleted file mode 100644 index 19bff270b6..0000000000 --- a/frontend/src/component/feature/add-tag-dialog-component.jsx +++ /dev/null @@ -1,126 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; - -import { DialogContentText, Button, TextField } from '@material-ui/core'; -import Dialogue from '../common/Dialogue'; -import { trim } from '../common/util'; -import TagSelect from '../common/TagSelect/TagSelect'; - -import styles from './add-tag-dialog-component.module.scss'; - -class AddTagDialogComponent extends Component { - constructor(props) { - super(props); - this.state = { - openDialog: false, - errors: {}, - currentLegalValue: '', - dirty: false, - featureToggleName: props.featureToggleName, - tag: props.tag, - }; - } - - handleOpenDialog() { - this.setState({ openDialog: true }); - } - handleCancel() { - this.setState({ - openDialog: false, - tag: { type: 'simple', value: '' }, - }); - } - - setValue = (field, value) => { - const { tag } = this.state; - tag[field] = trim(value); - this.setState({ tag, dirty: true }); - }; - - onCancel = evt => { - evt.preventDefault(); - this.setState({ - openDialog: false, - tag: { type: 'simple', value: '' }, - }); - }; - onSubmit = async evt => { - evt.preventDefault(); - const { tag } = this.state; - if (!tag.type) { - tag.type = 'simple'; - } - try { - await this.props.submit(this.props.featureToggleName, tag); - this.setState({ - openDialog: false, - tag: { type: 'simple', value: '' }, - }); - } catch (e) { - this.setState({ errors: { general: e.message } }); - } - }; - render() { - const { tag, errors, openDialog } = this.state; - const formId = 'add-tag-dialog-form'; - return ( - - - - - <> - - Tags allow you to group features together - -
-
- - this.setValue('type', v.target.value) - } - /> -
- - this.setValue('value', v.target.value) - } - /> -
- {errors.general && ( -

{errors.general}

- )} -
- -
-
- ); - } -} - -AddTagDialogComponent.propTypes = { - featureToggleName: PropTypes.string.isRequired, - tag: PropTypes.object.isRequired, - submit: PropTypes.func.isRequired, -}; - -export default AddTagDialogComponent; diff --git a/frontend/src/component/feature/add-tag-dialog-component.module.scss b/frontend/src/component/feature/add-tag-dialog-component.module.scss deleted file mode 100644 index 73f0c9063a..0000000000 --- a/frontend/src/component/feature/add-tag-dialog-component.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.dialogueFormContent > * { - margin: 0.5rem 0; -} \ No newline at end of file diff --git a/frontend/src/component/feature/add-tag-dialog-container.js b/frontend/src/component/feature/add-tag-dialog-container.js deleted file mode 100644 index f99e7dd57e..0000000000 --- a/frontend/src/component/feature/add-tag-dialog-container.js +++ /dev/null @@ -1,16 +0,0 @@ -import { connect } from 'react-redux'; -import AddTagDialogComponent from './add-tag-dialog-component'; - -import { tagFeature } from '../../store/feature-tags/actions'; - -const mapStateToProps = () => ({ - tag: { type: 'simple', value: '' }, -}); - -const mapDispatchToProps = dispatch => ({ - submit: (featureToggleName, tag) => tagFeature(featureToggleName, tag)(dispatch), -}); - -const AddTagDialogContainer = connect(mapStateToProps, mapDispatchToProps)(AddTagDialogComponent); - -export default AddTagDialogContainer; diff --git a/frontend/src/component/feature/create/CreateFeature/CreateFeature.jsx b/frontend/src/component/feature/create/CreateFeature/CreateFeature.jsx deleted file mode 100644 index 967410802a..0000000000 --- a/frontend/src/component/feature/create/CreateFeature/CreateFeature.jsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useEffect } from 'react'; -import { useParams } from 'react-router'; -import PropTypes from 'prop-types'; -import { CardActions, Switch, TextField } from '@material-ui/core'; -import FeatureTypeSelect from '../../feature-type-select-container'; -import ProjectSelect from '../../project-select-container'; -import StrategiesList from '../../strategy/strategies-list-container'; -import PageContent from '../../../common/PageContent/PageContent'; - -import { FormButtons, styles as commonStyles } from '../../../common'; -import { trim } from '../../../common/util'; - -import styles from '../add-feature-component.module.scss'; -import { - CF_CREATE_BTN_ID, - CF_DESC_ID, - CF_NAME_ID, - CF_TYPE_ID, -} from '../../../../testIds'; -import { CREATE_FEATURE } from '../../../providers/AccessProvider/permissions'; -import { projectFilterGenerator } from '../../../../utils/project-filter-generator'; -import { useHistory } from 'react-router-dom'; - -const CreateFeature = ({ - input, - errors, - setValue, - validateName, - onSubmit, - user, -}) => { - const { projectId } = useParams(); - - const history = useHistory(); - - useEffect(() => { - if (projectId) { - setValue('project', projectId); - } - /* eslint-disable-next-line */ - }, []); - - useEffect(() => { - window.onbeforeunload = () => - 'Data will be lost if you leave the page, are you sure?'; - - return () => { - window.onbeforeunload = false; - }; - }, []); - - const onCancel = () => history.goBack(); - - return ( - -
-
- validateName(v.target.value)} - onChange={v => setValue('name', trim(v.target.value))} - /> -
-
- setValue('type', v.target.value)} - label={'Toggle type'} - id="feature-type-select" - editable - inputProps={{ - 'data-test': CF_TYPE_ID, - }} - /> -
-
- setValue('project', v.target.value)} - filter={projectFilterGenerator(user, CREATE_FEATURE)} - /> -
-
- setValue('description', v.target.value)} - /> -
-
- { - setValue('enabled', !input.enabled); - }} - /> -

- {input.enabled ? 'Enabled' : 'Disabled'} feature toggle -

-
-
- setValue('strategies', s)} - editable - /> -
- - - -
-
- ); -}; - -CreateFeature.propTypes = { - input: PropTypes.object, - errors: PropTypes.object, - setValue: PropTypes.func.isRequired, - onSubmit: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, - validateName: PropTypes.func.isRequired, - initCallRequired: PropTypes.bool, - init: PropTypes.func, -}; - -export default CreateFeature; diff --git a/frontend/src/component/feature/create/CreateFeature/index.jsx b/frontend/src/component/feature/create/CreateFeature/index.jsx deleted file mode 100644 index ad6de0bd91..0000000000 --- a/frontend/src/component/feature/create/CreateFeature/index.jsx +++ /dev/null @@ -1,149 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import { - createFeatureToggles, - validateName, -} from '../../../../store/feature-toggle/actions'; -import CreateFeature from './CreateFeature'; -import { loadNameFromUrl, showPnpsFeedback } from '../../../common/util'; -import { showFeedback } from '../../../../store/feedback/actions'; -import { getTogglePath } from '../../../../utils/route-path-helpers'; - -const defaultStrategy = { - name: 'default', - parameters: {}, -}; - -function resolveCurrentProjectId(settings) { - if (!settings.currentProjectId) { - return 'default'; - } else if (settings.currentProjectId === '*') { - return 'default'; - } else { - return settings.currentProjectId; - } -} - -class WrapperComponent extends Component { - constructor(props) { - super(); - const name = loadNameFromUrl(); - this.state = { - featureToggle: { - name, - description: '', - type: 'release', - stale: false, - strategies: [], - variants: [], - enabled: true, - project: props.currentProjectId, - }, - errors: {}, - dirty: false, - }; - } - - setValue = (field, value) => { - const { featureToggle } = this.state; - featureToggle[field] = value; - this.setState({ featureToggle, dirty: true }); - }; - - validateName = async featureToggleName => { - if (featureToggleName.length > 0) { - const { errors } = { ...this.state }; - try { - await validateName(featureToggleName); - errors.name = undefined; - } catch (err) { - errors.name = err.message; - } - - this.setState({ errors }); - } - }; - - onSubmit = async evt => { - const { user } = this.props; - evt.preventDefault(); - const { createFeatureToggles, history, showFeedback } = this.props; - const { featureToggle } = this.state; - - const errors = Object.values(this.state.errors).filter(i => i); - - if (errors.length > 0) { - return; - } - - if (featureToggle.strategies < 1) { - featureToggle.strategies = [defaultStrategy]; - } - - try { - await createFeatureToggles(featureToggle).then(() => - history.push( - getTogglePath(featureToggle.project, featureToggle.name) - ) - ); - - if (showPnpsFeedback(user)) { - showFeedback(); - } - // Trigger - } catch (e) { - if (e.toString().includes('not allowed to be empty')) { - this.setState({ - errors: { name: 'Name is not allowed to be empty' }, - }); - } - } - }; - - onCancel = evt => { - evt.preventDefault(); - this.props.history.goBack(); - }; - - render() { - return ( - - ); - } -} -WrapperComponent.propTypes = { - history: PropTypes.object.isRequired, - createFeatureToggles: PropTypes.func.isRequired, - currentProjectId: PropTypes.string.isRequired, -}; - -const mapDispatchToProps = dispatch => ({ - validateName: name => validateName(name)(dispatch), - createFeatureToggles: featureToggle => - createFeatureToggles(featureToggle)(dispatch), - showFeedback: showFeedback(dispatch), -}); - -const mapStateToProps = state => { - const settings = state.settings.toJS().feature || {}; - const currentProjectId = resolveCurrentProjectId(settings); - - return { currentProjectId, user: state.user.toJS() }; -}; - -const FormAddContainer = connect( - mapStateToProps, - mapDispatchToProps -)(WrapperComponent); - -export default FormAddContainer; diff --git a/frontend/src/component/feature/create/__tests__/.eslintrc b/frontend/src/component/feature/create/__tests__/.eslintrc deleted file mode 100644 index eba2077219..0000000000 --- a/frontend/src/component/feature/create/__tests__/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "jest": true - } -} diff --git a/frontend/src/component/feature/create/add-feature-component.module.scss b/frontend/src/component/feature/create/add-feature-component.module.scss deleted file mode 100644 index efaf1e98a2..0000000000 --- a/frontend/src/component/feature/create/add-feature-component.module.scss +++ /dev/null @@ -1,31 +0,0 @@ -.header { - font-size: var(--h1-size); - padding: var(--card-header-padding); -} - -.container { - padding: var(--card-padding); -} - -.nameInput { - margin-right: 1.5rem; - min-width: 250px; -} - -.formContainer { - margin-bottom: 1.5rem; - max-width: 350px; -} - -.legalValueFormContainer { - display: flex; -} - -.toggleContainer { - display: flex; - align-items: center; -} - -.strategiesContainer { - margin: 2rem 0; -} diff --git a/frontend/src/component/feature/feature-tag-component.jsx b/frontend/src/component/feature/feature-tag-component.jsx deleted file mode 100644 index 3fe4699301..0000000000 --- a/frontend/src/component/feature/feature-tag-component.jsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useState } from 'react'; -import PropTypes from 'prop-types'; -import { Chip } from '@material-ui/core'; -import { Label } from '@material-ui/icons'; - -import ConditionallyRender from '../common/ConditionallyRender/ConditionallyRender'; -import Dialogue from '../common/Dialogue'; - -import slackIcon from '../../assets/icons/slack.svg'; -import jiraIcon from '../../assets/icons/jira.svg'; -import webhookIcon from '../../assets/icons/webhooks.svg'; -import { formatAssetPath } from '../../utils/format-path'; - -function FeatureTagComponent({ - tags, - tagTypes, - featureToggleName, - untagFeature, -}) { - const [showDialog, setShowDialog] = useState(false); - const [selectedTag, setSelectedTag] = useState(undefined); - const onUntagFeature = tag => { - // eslint-disable-next-line no-alert - untagFeature(featureToggleName, tag); - setSelectedTag(undefined); - }; - - const tagIcon = typeName => { - let tagType = tagTypes.find(type => type.name === typeName); - - const style = { width: '20px', height: '20px', marginRight: '5px' }; - - if (tagType && tagType.icon) { - switch (tagType.name) { - case 'slack': - return ( - slack - ); - case 'jira': - return ( - jira - ); - case 'webhook': - return ( - webhook - ); - default: - return
- Read more - - - -
- -`; - -exports[`renders correctly with without variants 1`] = ` -
-

- Variants allows you to return a variant object if the feature toggle is considered enabled for the current request. When using variants you should use the - - - getVariant() - - method in the Client SDK. -

-

- No variants defined. -

-
-
- -
-
-`; - -exports[`renders correctly without variants and no permissions 1`] = ` -
-

- Variants allows you to return a variant object if the feature toggle is considered enabled for the current request. When using variants you should use the - - - getVariant() - - method in the Client SDK. -

-

- No variants defined. -

-
-
- -
-
-`; diff --git a/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx b/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx deleted file mode 100644 index c6a4aefdcd..0000000000 --- a/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx +++ /dev/null @@ -1,134 +0,0 @@ -import { MemoryRouter } from 'react-router-dom'; -import { ThemeProvider } from '@material-ui/core'; - -import UpdateVariant from './../update-variant-component'; -import renderer from 'react-test-renderer'; -import { weightTypes } from '../enums'; -import theme from '../../../../themes/main-theme'; -import { Provider } from 'react-redux'; -import { createStore } from 'redux'; - -jest.mock( - '../AddVariant/OverrideConfig/OverrideConfig.jsx', - () => 'OverrideConfig' -); - -const mockStore = { - uiConfig: { - toJS: () => ({ - flags: { - P: true, - }, - }), - }, -}; - -const mockReducer = state => state; - -test('renders correctly with without variants', () => { - const tree = renderer.create( - - - - - - - - ); - - expect(tree).toMatchSnapshot(); -}); - -test('renders correctly without variants and no permissions', () => { - const tree = renderer.create( - - - - - - - - ); - - expect(tree).toMatchSnapshot(); -}); - -test('renders correctly with with variants', () => { - const featureToggle = { - name: 'toggle.variants', - description: 'description', - enabled: false, - strategies: [ - { - name: 'gradualRolloutRandom', - parameters: { - percentage: 50, - }, - }, - ], - variants: [ - { - name: 'blue', - weight: 34, - overrides: [ - { - field: 'userId', - values: ['1337', '123'], - }, - ], - }, - { - name: 'yellow', - weight: 33, - }, - { - name: 'orange', - weight: 33, - weightType: weightTypes.FIX, - payload: { - type: 'string', - value: '{"color": "blue", "animated": false}', - }, - }, - ], - createdAt: '2018-02-04T20:27:52.127Z', - }; - const tree = renderer.create( - - - - - - - - ); - - expect(tree).toMatchSnapshot(); -}); diff --git a/frontend/src/component/feature/variant/update-variant-component.jsx b/frontend/src/component/feature/variant/update-variant-component.jsx deleted file mode 100644 index 9cf9777c55..0000000000 --- a/frontend/src/component/feature/variant/update-variant-component.jsx +++ /dev/null @@ -1,209 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; - -import VariantViewComponent from './variant-view-component'; -import styles from './variant.module.scss'; -import { - Table, - TableHead, - TableRow, - TableCell, - TableBody, - Button, - Typography, -} from '@material-ui/core'; -import AddVariant from './AddVariant/AddVariant'; - -import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; -import GeneralSelect from '../../common/GeneralSelect/GeneralSelect'; - -const initialState = { - showDialog: false, - editVariant: undefined, - editIndex: -1, -}; - -class UpdateVariantComponent extends Component { - constructor(props) { - super(props); - this.state = { ...initialState }; - } - - closeDialog = () => { - this.setState({ ...initialState }); - }; - - openAddVariant = e => { - e.preventDefault(); - this.setState({ - showDialog: true, - editVariant: undefined, - editIndex: undefined, - title: 'Add variant', - }); - }; - - openEditVariant = (e, index, variant) => { - e.preventDefault(); - if (this.props.editable) { - this.setState({ - showDialog: true, - editVariant: variant, - editIndex: index, - title: 'Edit variant', - }); - } - }; - - validateName = name => { - if (!name) { - return { name: 'Name is required' }; - } - }; - - onRemoveVariant = (e, index) => { - e.preventDefault(); - try { - this.props.removeVariant(index); - } catch (e) { - console.log('An exception was caught.'); - } - }; - - renderVariant = (variant, index) => ( - this.openEditVariant(e, index, variant)} - removeVariant={e => this.onRemoveVariant(e, index)} - editable={this.props.editable} - /> - ); - - renderVariants = variants => ( - - - - Variant name - - Weight - Weight Type - - - - {variants.map(this.renderVariant)} -
- ); - - renderStickiness = variants => { - const { updateStickiness, stickinessOptions } = this.props; - - if (!variants || variants.length < 2) { - return null; - } - - const value = variants[0].stickiness || 'default'; - const options = stickinessOptions.map(c => ({ key: c, label: c })); - - // guard on stickiness being disabled for context field. - if (!stickinessOptions.includes(value)) { - options.push({ key: value, label: value }); - } - - const onChange = event => updateStickiness(event.target.value); - - return ( -
- -    - - By overriding the stickiness you can control which parameter - you want to be used in order to ensure consistent traffic - allocation across variants.{' '} - - Read more - - -
- ); - }; - - render() { - const { showDialog, editVariant, editIndex, title } = this.state; - const { variants, addVariant, updateVariant } = this.props; - const saveVariant = editVariant - ? updateVariant.bind(null, editIndex) - : addVariant; - - return ( -
- - Variants allows you to return a variant object if the - feature toggle is considered enabled for the current - request. When using variants you should use the{' '} - getVariant() method - in the Client SDK. - - - 0} - show={this.renderVariants(variants)} - elseShow={

No variants defined.

} - /> - -
- - - {this.renderStickiness(variants)} - - } - /> - - -
- ); - } -} - -UpdateVariantComponent.propTypes = { - variants: PropTypes.array.isRequired, - addVariant: PropTypes.func.isRequired, - removeVariant: PropTypes.func.isRequired, - updateVariant: PropTypes.func.isRequired, - updateStickiness: PropTypes.func.isRequired, - editable: PropTypes.bool.isRequired, - stickinessOptions: PropTypes.array, -}; - -export default UpdateVariantComponent; diff --git a/frontend/src/component/feature/variant/update-variant-container.jsx b/frontend/src/component/feature/variant/update-variant-container.jsx deleted file mode 100644 index 2116cca026..0000000000 --- a/frontend/src/component/feature/variant/update-variant-container.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import { connect } from 'react-redux'; - -import { requestUpdateFeatureToggleVariants } from '../../../store/feature-toggle/actions'; -import UpdateFeatureToggleComponent from './update-variant-component'; -import { updateWeight } from '../../common/util'; - -const mapStateToProps = (state, ownProps) => ({ - variants: ownProps.featureToggle.variants || [], - features: state.features.toJS(), - stickinessOptions: [ - 'default', - ...state.context.filter(c => c.stickiness).map(c => c.name), - ], -}); - -const mapDispatchToProps = (dispatch, ownProps) => ({ - addVariant: variant => { - const { featureToggle } = ownProps; - const currentVariants = featureToggle.variants || []; - let stickiness; - if (currentVariants.length > 0) { - stickiness = currentVariants[0].stickiness || 'default'; - } else { - stickiness = 'default'; - } - variant.stickiness = stickiness; - - const variants = [...currentVariants, variant]; - updateWeight(variants, 1000); - return requestUpdateFeatureToggleVariants( - featureToggle, - variants - )(dispatch); - }, - removeVariant: index => { - const { featureToggle } = ownProps; - const currentVariants = featureToggle.variants || []; - - const variants = currentVariants.filter((v, i) => i !== index); - if (variants.length > 0) { - updateWeight(variants, 1000); - } - requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch); - }, - updateVariant: (index, variant) => { - const { featureToggle } = ownProps; - const currentVariants = featureToggle.variants || []; - const variants = currentVariants.map((v, i) => - i === index ? variant : v - ); - updateWeight(variants, 1000); - requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch); - }, - updateStickiness: stickiness => { - const { featureToggle } = ownProps; - const currentVariants = featureToggle.variants || []; - const variants = currentVariants.map(v => ({ ...v, stickiness })); - requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch); - }, -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(UpdateFeatureToggleComponent); diff --git a/frontend/src/component/feature/variant/variant-view-component.jsx b/frontend/src/component/feature/variant/variant-view-component.jsx deleted file mode 100644 index c4310f5085..0000000000 --- a/frontend/src/component/feature/variant/variant-view-component.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { IconButton, Chip, TableCell, TableRow } from '@material-ui/core'; -import { Edit, Delete } from '@material-ui/icons'; - -import { weightTypes } from './enums'; - -import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; - -import styles from './variant.module.scss'; -function VariantViewComponent({ - variant, - editVariant, - removeVariant, - editable, -}) { - const { FIX } = weightTypes; - return ( - - {variant.name} - - } - /> - 0 - } - show={ - - } - /> - - {variant.weight / 10.0} % - - {variant.weightType === FIX ? 'Fix' : 'Variable'} - - -
- - - - - - -
- - } - elseShow={} - /> -
- ); -} - -VariantViewComponent.propTypes = { - variant: PropTypes.object, - removeVariant: PropTypes.func, - editVariant: PropTypes.func, - editable: PropTypes.bool.isRequired, -}; - -export default VariantViewComponent; diff --git a/frontend/src/component/feature/variant/variant.module.scss b/frontend/src/component/feature/variant/variant.module.scss deleted file mode 100644 index 8110253b45..0000000000 --- a/frontend/src/component/feature/variant/variant.module.scss +++ /dev/null @@ -1,100 +0,0 @@ -.variantTable { - width: 100%; - max-width: 700px; - - th, - td { - text-align: center; - } - th:first-of-type, - td:first-of-type { - text-align: left; - width: 100%; - } - tbody tr:hover { - background-color: rgba(173, 216, 230, 0.2); - } -} - -@media (max-width: 600px) { - th.labels { - display: none; - } - td.labels { - display: none; - } -} - -th.labels { - text-align: right; -} - -td.labels { - text-align: right; - vertical-align: top; -} - -th.actions { - text-align: right; -} - -td.actions { - height: 100%; - text-align: right; - vertical-align: top; -} - -.actionsContainer { - display: flex; - align-items: center; -} - -.modal { - max-width: 90%; - width: 600px; - position: absolute !important; -} - -@media (max-width: 600px) { - .modal { - top: 0 !important; - } -} - -.tooltip { - i { - font-size: 18px; - } -} - -.inputWeight { - text-align: right; -} - -.flexCenter { - display: flex; - justify-content: center; - align-items: center; -} - -.flex { - display: flex; - align-items: center; -} - -.marginL10 { - margin-left: 10px; -} - -.addVariantButton { - margin: 1rem 0; -} - -.paragraph { - max-width: 400px; -} - -.helperText { - display: block; - margin-top: 0.5rem; -} diff --git a/frontend/src/component/feature/view/__tests__/.eslintrc b/frontend/src/component/feature/view/__tests__/.eslintrc deleted file mode 100644 index eba2077219..0000000000 --- a/frontend/src/component/feature/view/__tests__/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "jest": true - } -} diff --git a/frontend/src/component/feature/view/__tests__/__snapshots__/update-strategies-component-test.jsx.snap b/frontend/src/component/feature/view/__tests__/__snapshots__/update-strategies-component-test.jsx.snap deleted file mode 100644 index b3bacd516f..0000000000 --- a/frontend/src/component/feature/view/__tests__/__snapshots__/update-strategies-component-test.jsx.snap +++ /dev/null @@ -1,27 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`render the create feature page 1`] = ` -
- -
-`; diff --git a/frontend/src/component/feature/view/__tests__/__snapshots__/view-component-test.jsx.snap b/frontend/src/component/feature/view/__tests__/__snapshots__/view-component-test.jsx.snap deleted file mode 100644 index fcf80e93cd..0000000000 --- a/frontend/src/component/feature/view/__tests__/__snapshots__/view-component-test.jsx.snap +++ /dev/null @@ -1,567 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly with one feature 1`] = ` -
-
-
-

- Another -

-
-
- - Active - -
-
-
-
-
-

- another's description -

-
-
-
- -
-
- Release -
- - - - -
- - - Feature type - - -
-
-
-   -
- -
-
- default -
- - - - -
- - - Project - - -
-
-
-
- -
-
-
- - - - - - - - - - - - Disabled - - -
- - - - Clone - - - - -
-
-
-
-
-
-
- - - - -
-
-
-
-
- - - ); - } -} diff --git a/frontend/src/component/feature/view/metric-container.jsx b/frontend/src/component/feature/view/metric-container.jsx deleted file mode 100644 index cb9857e6b3..0000000000 --- a/frontend/src/component/feature/view/metric-container.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import { connect } from 'react-redux'; - -import { fetchFeatureMetrics, fetchSeenApps } from '../../../store/feature-metrics/actions'; - -import MatricComponent from './metric-component'; - -function getMetricsForToggle(state, toggleName) { - if (!toggleName) { - return; - } - const result = {}; - - if (state.featureMetrics.hasIn(['seenApps', toggleName])) { - result.seenApps = state.featureMetrics.getIn(['seenApps', toggleName]); - } - if (state.featureMetrics.hasIn(['lastHour', toggleName])) { - result.lastHour = state.featureMetrics.getIn(['lastHour', toggleName]); - result.lastMinute = state.featureMetrics.getIn(['lastMinute', toggleName]); - } - return result; -} - -export default connect( - (state, props) => ({ - metrics: getMetricsForToggle(state, props.featureToggle.name), - location: state.settings.toJS().location || {}, - }), - { - fetchFeatureMetrics, - fetchSeenApps, - } -)(MatricComponent); diff --git a/frontend/src/component/feature/view/metric.module.scss b/frontend/src/component/feature/view/metric.module.scss deleted file mode 100644 index 7a84b6afb2..0000000000 --- a/frontend/src/component/feature/view/metric.module.scss +++ /dev/null @@ -1,41 +0,0 @@ -.chip { - display: inline-flex; - vertical-align: middle; - margin-left: 30px; - position: relative; - z-index: 1; - overflow: visible; -} - -.chip:first-child { - margin-left: 0; -} - -.chip:not(:first-child):after { - content: " OR "; - position: absolute; - left: -27px; - top: 0; - color: #ccc; - width: 25px; - line-height: 32px; - font-size: 14px; - text-align: center; - height: 100%; - z-index: 2; -} - -.chip:first-child:before { - content: ""; -} - -.problemIcon { - width: 100px; - height: 100px; - font-size: 100px !important; - color: #ccc; -} - -.grid { - text-align: center; -} diff --git a/frontend/src/component/feature/view/status-update-component.jsx b/frontend/src/component/feature/view/status-update-component.jsx deleted file mode 100644 index 8a350169c2..0000000000 --- a/frontend/src/component/feature/view/status-update-component.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { MenuItem } from '@material-ui/core'; -import DropdownMenu from '../../common/DropdownMenu/DropdownMenu'; -import PropTypes from 'prop-types'; - -export default function StatusUpdateComponent({ stale, updateStale }) { - function setStatus(field) { - if (field === 'active') { - updateStale(false); - } else if (field === 'stale') { - updateStale(true); - } - } - - const renderOptions = () => [ - - Set toggle Active - , - - Mark toggle as Stale - , - ]; - - const onClick = e => { - setStatus(e.target.getAttribute('data-target')); - }; - - return ( - - ); -} - -StatusUpdateComponent.propTypes = { - stale: PropTypes.bool.isRequired, - updateStale: PropTypes.func.isRequired, -}; diff --git a/frontend/src/component/feature/view/update-description-component.jsx b/frontend/src/component/feature/view/update-description-component.jsx deleted file mode 100644 index 907c8c6b8c..0000000000 --- a/frontend/src/component/feature/view/update-description-component.jsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; -import { - Typography, - IconButton, - FormControl, - TextField, - Button, -} from '@material-ui/core'; -import CreateIcon from '@material-ui/icons/Create'; -import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender'; - -import styles from './update-description-component.module.scss'; - -export default class UpdateDescriptionComponent extends React.Component { - constructor(props) { - super(props); - this.state = { editMode: false }; - } - - static propTypes = { - isFeatureView: PropTypes.bool.isRequired, - update: PropTypes.func, - featureToggle: PropTypes.object, - editable: PropTypes.bool, - }; - - onEditMode = (description, evt) => { - evt.preventDefault(); - this.setState({ editMode: true, description }); - }; - - updateValue = evt => { - evt.preventDefault(); - this.setState({ description: evt.target.value }); - }; - - onSave = evt => { - evt.preventDefault(); - this.props.update(this.state.description); - this.setState({ editMode: false, description: undefined }); - }; - - onCancel = evt => { - evt.preventDefault(); - this.setState({ editMode: false, description: undefined }); - }; - - renderRead({ description, editable }) { - return ( - - - {description || 'No feature toggle description'} - - - - } - /> - - - ); - } - - renderEdit() { - const { description } = this.state; - return ( -
- -
- -   - -
-
- ); - } - - render() { - const { editMode } = this.state; - return editMode - ? this.renderEdit(this.props) - : this.renderRead(this.props); - } -} diff --git a/frontend/src/component/feature/view/update-description-component.module.scss b/frontend/src/component/feature/view/update-description-component.module.scss deleted file mode 100644 index 2d1d2d2a85..0000000000 --- a/frontend/src/component/feature/view/update-description-component.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.descriptionInput { - width: 350px; -} diff --git a/frontend/src/component/feature/view/update-strategies-component.jsx b/frontend/src/component/feature/view/update-strategies-component.jsx deleted file mode 100644 index 3ae49d067d..0000000000 --- a/frontend/src/component/feature/view/update-strategies-component.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import StrategiesList from '../strategy/strategies-list-container'; - -// TODO: do we still need this wrapper? -function UpdateStrategiesComponent(props) { - const { configuredStrategies } = props; - if (!configuredStrategies || configuredStrategies.length === 0) return null; - - return ( -
- -
- ); -} - -UpdateStrategiesComponent.propTypes = { - featureToggleName: PropTypes.string.isRequired, - strategies: PropTypes.array, - configuredStrategies: PropTypes.array.isRequired, - editable: PropTypes.bool, -}; - -UpdateStrategiesComponent.defaultProps = { - editable: true, -}; - -export default UpdateStrategiesComponent; diff --git a/frontend/src/component/feature/view/update-strategies-container.jsx b/frontend/src/component/feature/view/update-strategies-container.jsx deleted file mode 100644 index 1b63833a90..0000000000 --- a/frontend/src/component/feature/view/update-strategies-container.jsx +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable no-console */ -import { connect } from 'react-redux'; - -import { requestUpdateFeatureToggleStrategies } from '../../../store/feature-toggle/actions'; -import UpdateStrategiesComponent from './update-strategies-component'; - -const mapStateToProps = (state, ownProps) => ({ - featureToggleName: ownProps.featureToggle.name, - configuredStrategies: ownProps.featureToggle.strategies, -}); - -const mapDispatchToProps = (dispatch, ownProps) => ({ - saveStrategies: strategies => { - const featureToggle = ownProps.featureToggle; - return requestUpdateFeatureToggleStrategies(featureToggle, strategies)(dispatch); - }, -}); - -export default connect(mapStateToProps, mapDispatchToProps)(UpdateStrategiesComponent); diff --git a/frontend/src/component/history/EventHistory/EventHistory.tsx b/frontend/src/component/history/EventHistory/EventHistory.tsx index 3821f8cb1d..ec989e9348 100644 --- a/frontend/src/component/history/EventHistory/EventHistory.tsx +++ b/frontend/src/component/history/EventHistory/EventHistory.tsx @@ -1,22 +1,12 @@ -import { useEffect } from 'react'; - import EventLog from '../EventLog'; +import { useEvents } from '../../../hooks/api/getters/useEvents/useEvents'; -interface IEventLogProps { - fetchHistory: () => void; - history: History; -} +export const EventHistory = () => { + const { events } = useEvents(); -const EventHistory = ({ fetchHistory, history }: IEventLogProps) => { - useEffect(() => { - fetchHistory(); - }, [fetchHistory]); - - if (history.length < 0) { + if (events.length < 0) { return null; } - return ; + return ; }; - -export default EventHistory; diff --git a/frontend/src/component/history/EventHistory/index.js b/frontend/src/component/history/EventHistory/index.js deleted file mode 100644 index 2cbef8ad75..0000000000 --- a/frontend/src/component/history/EventHistory/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import { connect } from 'react-redux'; -import EventHistory from './EventHistory'; -import { fetchHistory } from '../../../store/history/actions'; - -const mapStateToProps = state => { - const history = state.history.get('list').toArray(); - return { - history, - }; -}; - -const EventHistoryContainer = connect(mapStateToProps, { fetchHistory })( - EventHistory -); - -export default EventHistoryContainer; diff --git a/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx b/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx index 6b767ae72c..eac9a008a7 100644 --- a/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx +++ b/frontend/src/component/history/FeatureEventHistory/FeatureEventHistory.jsx @@ -1,29 +1,19 @@ import PropTypes from 'prop-types'; -import { useEffect } from 'react'; import EventLog from '../EventLog'; +import { useFeatureEvents } from '../../../hooks/api/getters/useFeatureEvents/useFeatureEvents'; -const FeatureEventHistory = ({ - toggleName, - history, - fetchHistoryForToggle, -}) => { - useEffect(() => { - fetchHistoryForToggle(toggleName); - }, [fetchHistoryForToggle, toggleName]); +export const FeatureEventHistory = ({ toggleName }) => { + const { events } = useFeatureEvents(toggleName); - if (!history || history.length === 0) { - return fetching..; + if (events.length === 0) { + return null; } return ( - + ); }; FeatureEventHistory.propTypes = { toggleName: PropTypes.string.isRequired, - history: PropTypes.array, - fetchHistoryForToggle: PropTypes.func.isRequired, }; - -export default FeatureEventHistory; diff --git a/frontend/src/component/history/FeatureEventHistory/index.jsx b/frontend/src/component/history/FeatureEventHistory/index.jsx deleted file mode 100644 index 797151f55b..0000000000 --- a/frontend/src/component/history/FeatureEventHistory/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { connect } from 'react-redux'; -import FeatureEventHistory from './FeatureEventHistory'; -import { fetchHistoryForToggle } from '../../../store/history/actions'; - -function getHistoryFromToggle(state, toggleName) { - if (!toggleName) { - return []; - } - - if (state.history.hasIn(['toggles', toggleName])) { - return state.history.getIn(['toggles', toggleName]).toArray(); - } - - return []; -} - -const mapStateToProps = (state, props) => ({ - history: getHistoryFromToggle(state, props.toggleName), -}); - -const FeatureEventHistoryContainer = connect(mapStateToProps, { - fetchHistoryForToggle, -})(FeatureEventHistory); - -export default FeatureEventHistoryContainer; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap index 8bccd68c6a..9b57beca74 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap @@ -43,7 +43,7 @@ Array [ "layout": "main", "menu": Object {}, "parent": "/projects/:id/features/:name/:activeTab", - "path": "/projects/:id/features2/:name/:activeTab/copy", + "path": "/projects/:id/features/:name/:activeTab/copy", "title": "Copy", "type": "protected", }, @@ -52,7 +52,7 @@ Array [ "layout": "main", "menu": Object {}, "parent": "/projects", - "path": "/projects/:projectId/features2/:featureId/settings", + "path": "/projects/:projectId/features/:featureId/settings", "title": "Edit Feature", "type": "protected", }, @@ -62,8 +62,8 @@ Array [ "layout": "main", "menu": Object {}, "parent": "/projects", - "path": "/projects/:projectId/features2/:featureId", - "title": "FeatureView2", + "path": "/projects/:projectId/features/:featureId", + "title": "FeatureView", "type": "protected", }, Object { @@ -89,7 +89,7 @@ Array [ "layout": "main", "menu": Object {}, "parent": "/features", - "path": "/projects/:projectId/features/:name", + "path": "/projects/:projectId/features2/:name", "title": ":name", "type": "protected", }, @@ -330,15 +330,6 @@ Array [ "title": "Event History", "type": "protected", }, - Object { - "component": [Function], - "layout": "main", - "menu": Object {}, - "parent": "/archive", - "path": "/projects/:id/archived/:name/:activeTab", - "title": ":name", - "type": "protected", - }, Object { "component": [Function], "layout": "main", diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js index b945b93b84..1baf6fa80c 100644 --- a/frontend/src/component/menu/routes.js +++ b/frontend/src/component/menu/routes.js @@ -1,22 +1,19 @@ import CopyFeatureToggle from '../../page/features/copy'; -import ViewFeatureToggle from '../../page/features/show'; import Features from '../../page/features'; import CreateStrategies from '../../page/strategies/create'; import StrategyView from '../../page/strategies/show'; import Strategies from '../../page/strategies'; import HistoryPage from '../../page/history'; import HistoryTogglePage from '../../page/history/toggle'; -import ShowArchive from '../../page/archive/show'; import Archive from '../../page/archive'; -import ContextFields from '../../page/context'; import ListTagTypes from '../../page/tag-types'; import Addons from '../../page/addons'; import AddonsCreate from '../../page/addons/create'; import AddonsEdit from '../../page/addons/edit'; import Admin from '../admin'; import AdminApi from '../admin/api'; -import AdminUsers from '../admin/users'; -import AdminInvoice from '../admin/invoice'; +import AdminInvoice from '../admin/invoice/InvoiceAdminPage'; +import AdminUsers from '../admin/users/UsersAdmin'; import AdminAuth from '../admin/auth'; import Login from '../user/Login/Login'; import { P, C, E, EEA, RE } from '../common/flags'; @@ -25,10 +22,9 @@ import ResetPassword from '../user/ResetPassword/ResetPassword'; import ForgottenPassword from '../user/ForgottenPassword/ForgottenPassword'; import ProjectListNew from '../project/ProjectList/ProjectList'; import Project from '../project/Project/Project'; -import RedirectFeatureViewPage from '../../page/features/redirect'; import RedirectArchive from '../feature/RedirectArchive/RedirectArchive'; import EnvironmentList from '../environments/EnvironmentList/EnvironmentList'; -import FeatureView2 from '../feature/FeatureView2/FeatureView2'; +import FeatureView from '../feature/FeatureView/FeatureView'; import ProjectRoles from '../admin/project-roles/ProjectRoles/ProjectRoles'; import CreateProjectRole from '../admin/project-roles/CreateProjectRole/CreateProjectRole'; import EditProjectRole from '../admin/project-roles/EditProjectRole/EditProjectRole'; @@ -47,6 +43,9 @@ import CreateFeature from '../feature/CreateFeature/CreateFeature/CreateFeature' import EditFeature from '../feature/CreateFeature/EditFeature/EditFeature'; import EditApplication from '../application/EditApplication'; import ApplicationList from '../application/ApplicationList'; +import ContextList from '../context/ContextList/ContextList'; +import RedirectFeatureView from '../feature/RedirectFeatureView/RedirectFeatureView'; + export const routes = [ // Project @@ -88,7 +87,7 @@ export const routes = [ menu: {}, }, { - path: '/projects/:id/features2/:name/:activeTab/copy', + path: '/projects/:id/features/:name/:activeTab/copy', parent: '/projects/:id/features/:name/:activeTab', title: 'Copy', component: CopyFeatureToggle, @@ -97,7 +96,7 @@ export const routes = [ menu: {}, }, { - path: '/projects/:projectId/features2/:featureId/settings', + path: '/projects/:projectId/features/:featureId/settings', parent: '/projects', title: 'Edit Feature', component: EditFeature, @@ -106,10 +105,10 @@ export const routes = [ menu: {}, }, { - path: '/projects/:projectId/features2/:featureId', + path: '/projects/:projectId/features/:featureId', parent: '/projects', - title: 'FeatureView2', - component: FeatureView2, + title: 'FeatureView', + component: FeatureView, type: 'protected', layout: 'main', flags: E, @@ -119,7 +118,7 @@ export const routes = [ path: '/projects/:id/features/:name/:activeTab', parent: '/projects', title: ':name', - component: ViewFeatureToggle, + component: FeatureView, type: 'protected', layout: 'main', menu: {}, @@ -134,10 +133,10 @@ export const routes = [ menu: {}, }, { - path: '/projects/:projectId/features/:name', + path: '/projects/:projectId/features2/:name', parent: '/features', title: ':name', - component: RedirectFeatureViewPage, + component: RedirectFeatureView, type: 'protected', layout: 'main', menu: {}, @@ -176,7 +175,7 @@ export const routes = [ path: '/features/:activeTab/:name', parent: '/features', title: ':name', - component: RedirectFeatureViewPage, + component: RedirectFeatureView, type: 'protected', layout: 'main', menu: {}, @@ -233,7 +232,7 @@ export const routes = [ { path: '/context', title: 'Context Fields', - component: ContextFields, + component: ContextList, type: 'protected', flag: C, layout: 'main', @@ -371,15 +370,6 @@ export const routes = [ }, // Archive - { - path: '/projects/:id/archived/:name/:activeTab', - title: ':name', - parent: '/archive', - component: ShowArchive, - type: 'protected', - layout: 'main', - menu: {}, - }, { path: '/archive', title: 'Archived Toggles', diff --git a/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx b/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx index 0424d2fdbb..99dd106dfa 100644 --- a/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx +++ b/frontend/src/component/project/Project/ProjectHealth/ProjectHealth.tsx @@ -1,40 +1,38 @@ -import useHealthReport from '../../../../hooks/api/getters/useHealthReport/useHealthReport'; +import { useHealthReport } from '../../../../hooks/api/getters/useHealthReport/useHealthReport'; import ApiError from '../../../common/ApiError/ApiError'; import ConditionallyRender from '../../../common/ConditionallyRender'; -import ReportCardContainer from '../../../Reporting/ReportCard/ReportCardContainer'; import ReportToggleList from '../../../Reporting/ReportToggleList/ReportToggleList'; +import { ReportCard } from '../../../Reporting/ReportCard/ReportCard'; interface ProjectHealthProps { projectId: string; } const ProjectHealth = ({ projectId }: ProjectHealthProps) => { - const { project, error, refetch } = useHealthReport(projectId); + const { healthReport, refetchHealthReport, error } = + useHealthReport(projectId); + + if (!healthReport) { + return null; + } return (
} /> - +
); diff --git a/frontend/src/component/user/UserProfile/UserProfile.jsx b/frontend/src/component/user/UserProfile/UserProfile.tsx similarity index 89% rename from frontend/src/component/user/UserProfile/UserProfile.jsx rename to frontend/src/component/user/UserProfile/UserProfile.tsx index 7d729a5dbe..7f7ac5101a 100644 --- a/frontend/src/component/user/UserProfile/UserProfile.jsx +++ b/frontend/src/component/user/UserProfile/UserProfile.tsx @@ -8,15 +8,20 @@ import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; import { useStyles } from './UserProfile.styles'; import { useCommonStyles } from '../../../common.styles'; import UserProfileContent from './UserProfileContent/UserProfileContent'; +import { IUser } from "../../../interfaces/user"; + +interface IUserProfileProps { + profile: IUser + updateSettingLocation: (field: 'locale', value: string) => void +} const UserProfile = ({ profile, location, - fetchUser, updateSettingLocation, -}) => { +}: IUserProfileProps) => { const [showProfile, setShowProfile] = useState(false); - const [currentLocale, setCurrentLocale] = useState([]); + const [currentLocale, setCurrentLocale] = useState(); const styles = useStyles(); const commonStyles = useCommonStyles(); @@ -35,8 +40,7 @@ const UserProfile = ({ ]); useEffect(() => { - fetchUser(); - const locale = navigator.language || navigator.userLanguage; + const locale = location.locale || navigator.language; let found = possibleLocales.find(l => l.toLowerCase().includes(locale.toLowerCase()) ); @@ -73,7 +77,6 @@ const UserProfile = ({ profile={profile} updateSettingLocation={updateSettingLocation} possibleLocales={possibleLocales} - location={location} setCurrentLocale={setCurrentLocale} currentLocale={currentLocale} /> @@ -85,7 +88,6 @@ const UserProfile = ({ UserProfile.propTypes = { profile: PropTypes.object, location: PropTypes.object, - fetchUser: PropTypes.func.isRequired, updateSettingLocation: PropTypes.func.isRequired, }; diff --git a/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx b/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.tsx similarity index 86% rename from frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx rename to frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.tsx index 61119e4ea1..1d8b63b020 100644 --- a/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.jsx +++ b/frontend/src/component/user/UserProfile/UserProfileContent/UserProfileContent.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import React, { useState } from 'react'; import ConditionallyRender from '../../../common/ConditionallyRender'; import { Paper, @@ -17,6 +17,17 @@ import EditProfile from '../EditProfile/EditProfile'; import legacyStyles from '../../user.module.scss'; import { getBasePath } from '../../../../utils/format-path'; import useUiConfig from '../../../../hooks/api/getters/useUiConfig/useUiConfig'; +import { IUser } from "../../../../interfaces/user"; + +interface IUserProfileContentProps { + showProfile: boolean + profile: IUser + possibleLocales: string[] + updateSettingLocation: (field: 'locale', value: string) => void + imageUrl: string + currentLocale?: string + setCurrentLocale: (value: string) => void +} const UserProfileContent = ({ showProfile, @@ -26,27 +37,31 @@ const UserProfileContent = ({ imageUrl, currentLocale, setCurrentLocale, -}) => { +}: IUserProfileContentProps) => { const commonStyles = useCommonStyles(); const { uiConfig } = useUiConfig(); const [updatedPassword, setUpdatedPassword] = useState(false); - const [edititingProfile, setEditingProfile] = useState(false); + const [editingProfile, setEditingProfile] = useState(false); const styles = useStyles(); - const setLocale = value => { + const setLocale = (value: string) => { updateSettingLocation('locale', value); }; + // @ts-expect-error const profileAvatarClasses = classnames(styles.avatar, { - [styles.editingAvatar]: edititingProfile, + // @ts-expect-error + [styles.editingAvatar]: editingProfile, }); + // @ts-expect-error const profileEmailClasses = classnames(styles.profileEmail, { - [styles.editingEmail]: edititingProfile, + // @ts-expect-error + [styles.editingEmail]: editingProfile, }); - const handleChange = e => { - const { value } = e.target; + const handleChange = (e: React.ChangeEvent<{ value: unknown }>) => { + const value = e.target.value as string; setCurrentLocale(value); setLocale(value); }; @@ -57,6 +72,7 @@ const UserProfileContent = ({ show={
({ - profile: state.user.get('profile'), location: state.settings ? state.settings.toJS().location : {}, }); -export default connect(mapStateToProps, mapDispatchToProps)(UserProfile); +export default connect( + mapStateToProps, + mapDispatchToProps +)(props => { + const user = useUser(); + + return ( + + ); +}); diff --git a/frontend/src/hooks/api/getters/useEvents/useEvents.ts b/frontend/src/hooks/api/getters/useEvents/useEvents.ts new file mode 100644 index 0000000000..7c64b21271 --- /dev/null +++ b/frontend/src/hooks/api/getters/useEvents/useEvents.ts @@ -0,0 +1,39 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useCallback } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; +import { IEvent } from '../../../../interfaces/event'; + +const PATH = formatApiPath('api/admin/events'); + +export interface IUseEventsOutput { + events: IEvent[]; + refetchEvents: () => void; + loading: boolean; + error?: Error; +} + +export const useEvents = (options?: SWRConfiguration): IUseEventsOutput => { + const { data, error } = useSWR<{ events: IEvent[] }>( + PATH, + fetchAllEvents, + options + ); + + const refetchEvents = useCallback(() => { + mutate(PATH).catch(console.warn); + }, []); + + return { + events: data?.events || [], + loading: !error && !data, + refetchEvents, + error, + }; +}; + +const fetchAllEvents = () => { + return fetch(PATH, { method: 'GET' }) + .then(handleErrorResponses('Event history')) + .then(res => res.json()); +}; diff --git a/frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts b/frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts new file mode 100644 index 0000000000..3aa76433ec --- /dev/null +++ b/frontend/src/hooks/api/getters/useFeatureEvents/useFeatureEvents.ts @@ -0,0 +1,42 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useCallback } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; +import { IEvent } from '../../../../interfaces/event'; + +const PATH = formatApiPath('api/admin/events'); + +export interface IUseEventsOutput { + events: IEvent[]; + refetchEvents: () => void; + loading: boolean; + error?: Error; +} + +export const useFeatureEvents = ( + featureName: string, + options?: SWRConfiguration +): IUseEventsOutput => { + const { data, error } = useSWR<{ events: IEvent[] }>( + [PATH, featureName], + () => fetchFeatureEvents(featureName), + options + ); + + const refetchEvents = useCallback(() => { + mutate(PATH).catch(console.warn); + }, []); + + return { + events: data?.events || [], + loading: !error && !data, + refetchEvents, + error, + }; +}; + +const fetchFeatureEvents = (featureName: string) => { + return fetch(`${PATH}/${featureName}`, { method: 'GET' }) + .then(handleErrorResponses('Event history')) + .then(res => res.json()); +}; diff --git a/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts b/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts new file mode 100644 index 0000000000..ac7e290741 --- /dev/null +++ b/frontend/src/hooks/api/getters/useFeatures/useFeatures.ts @@ -0,0 +1,40 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useState, useEffect } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const useFeatures = (options: SWRConfiguration = {}) => { + const fetcher = async () => { + const path = formatApiPath('api/admin/features/'); + return fetch(path, { + method: 'GET', + }) + .then(handleErrorResponses('Features')) + .then(res => res.json()); + }; + + const FEATURES_CACHE_KEY = 'api/admin/features/'; + + const { data, error } = useSWR(FEATURES_CACHE_KEY, fetcher, { + ...options, + }); + + const [loading, setLoading] = useState(!error && !data); + + const refetchFeatures = () => { + mutate(FEATURES_CACHE_KEY); + }; + + useEffect(() => { + setLoading(!error && !data); + }, [data, error]); + + return { + features: data?.features || [], + error, + loading, + refetchFeatures, + }; +}; + +export default useFeatures; diff --git a/frontend/src/hooks/api/getters/useHealthReport/useHealthReport.ts b/frontend/src/hooks/api/getters/useHealthReport/useHealthReport.ts index 9640f91a05..75c5229bbb 100644 --- a/frontend/src/hooks/api/getters/useHealthReport/useHealthReport.ts +++ b/frontend/src/hooks/api/getters/useHealthReport/useHealthReport.ts @@ -1,51 +1,42 @@ import useSWR, { mutate, SWRConfiguration } from 'swr'; -import { useState, useEffect } from 'react'; +import { useCallback } from 'react'; import { IProjectHealthReport } from '../../../../interfaces/project'; -import { fallbackProject } from '../useProject/fallbackProject'; -import useSort from '../../../useSort'; import { formatApiPath } from '../../../../utils/format-path'; import handleErrorResponses from '../httpErrorResponseHandler'; -const useHealthReport = (id: string, options: SWRConfiguration = {}) => { - const KEY = `api/admin/projects/${id}/health-report`; +interface IUseHealthReportOutput { + healthReport: IProjectHealthReport | undefined; + refetchHealthReport: () => void; + loading: boolean; + error?: Error; +} - const fetcher = () => { - const path = formatApiPath(`api/admin/projects/${id}/health-report`); - return fetch(path, { - method: 'GET', - }) - .then(handleErrorResponses('Health report')) - .then(res => res.json()); - }; +export const useHealthReport = ( + projectId: string, + options?: SWRConfiguration +): IUseHealthReportOutput => { + const path = formatApiPath(`api/admin/projects/${projectId}/health-report`); - const [sort] = useSort(); + const { data, error } = useSWR( + path, + fetchHealthReport, + options + ); - const { data, error } = useSWR(KEY, fetcher, options); - const [loading, setLoading] = useState(!error && !data); - - const refetch = () => { - mutate(KEY); - }; - - useEffect(() => { - setLoading(!error && !data); - }, [data, error]); - - const sortedData = ( - data: IProjectHealthReport | undefined - ): IProjectHealthReport => { - if (data) { - return { ...data, features: sort(data.features || []) }; - } - return fallbackProject; - }; + const refetchHealthReport = useCallback(() => { + mutate(path).catch(console.warn); + }, [path]); return { - project: sortedData(data), + healthReport: data, + refetchHealthReport, + loading: !error && !data, error, - loading, - refetch, }; }; -export default useHealthReport; +const fetchHealthReport = (path: string): Promise => { + return fetch(path) + .then(handleErrorResponses('Health report')) + .then(res => res.json()); +}; diff --git a/frontend/src/hooks/api/getters/useInvoices/useInvoices.ts b/frontend/src/hooks/api/getters/useInvoices/useInvoices.ts new file mode 100644 index 0000000000..4f12c5a850 --- /dev/null +++ b/frontend/src/hooks/api/getters/useInvoices/useInvoices.ts @@ -0,0 +1,37 @@ +import useSWR, { mutate, SWRConfiguration } from 'swr'; +import { useState, useEffect } from 'react'; +import { formatApiPath } from '../../../../utils/format-path'; +import handleErrorResponses from '../httpErrorResponseHandler'; + +const KEY = `api/admin/invoices`; +const path = formatApiPath(KEY); + +const useInvoices = (options: SWRConfiguration = {}) => { + const fetcher = () => { + return fetch(path, { + method: 'GET', + }) + .then(handleErrorResponses('Invoices')) + .then(res => res.json()); + }; + + const { data, error } = useSWR(KEY, fetcher, options); + const [loading, setLoading] = useState(!error && !data); + + const refetchInvoices = () => { + mutate(KEY); + }; + + useEffect(() => { + setLoading(!error && !data); + }, [data, error]); + + return { + invoices: data?.invoices || [], + error, + loading, + refetchInvoices, + }; +}; + +export default useInvoices; diff --git a/frontend/src/interfaces/event.ts b/frontend/src/interfaces/event.ts new file mode 100644 index 0000000000..6626cc5d52 --- /dev/null +++ b/frontend/src/interfaces/event.ts @@ -0,0 +1,14 @@ +import { ITag } from './tags'; + +export interface IEvent { + id: number; + createdAt: string; + type: string; + createdBy: string; + project?: string; + environment?: string; + featureName?: string; + data?: any; + preData?: any; + tags?: ITag[]; +} diff --git a/frontend/src/interfaces/invoice.ts b/frontend/src/interfaces/invoice.ts new file mode 100644 index 0000000000..db49e34d9b --- /dev/null +++ b/frontend/src/interfaces/invoice.ts @@ -0,0 +1,8 @@ +export interface IInvoice { + amountFomratted: string; + invoicePDF: string; + invoiceURL: string; + paid: boolean; + status: string; + dueDate?: Date; +} diff --git a/frontend/src/interfaces/project.ts b/frontend/src/interfaces/project.ts index a915cd1996..835ba2845b 100644 --- a/frontend/src/interfaces/project.ts +++ b/frontend/src/interfaces/project.ts @@ -24,7 +24,7 @@ export interface IProjectHealthReport extends IProject { staleCount: number; potentiallyStaleCount: number; activeCount: number; - updatedAt: Date; + updatedAt: string; } export interface IPermission { diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index e64ab64fa1..70ea96657a 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -8,6 +8,7 @@ export interface IUiConfig { version: string; versionInfo: IVersionInfo; links: ILinks[]; + disablePasswordAuth?: boolean; } export interface IFlags { diff --git a/frontend/src/page/archive/show.js b/frontend/src/page/archive/show.js deleted file mode 100644 index d937968f3c..0000000000 --- a/frontend/src/page/archive/show.js +++ /dev/null @@ -1,20 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import ViewFeatureToggle from './../../component/archive/view-container'; -export default class Features extends PureComponent { - static propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, - }; - - render() { - const { match, history } = this.props; - return ( - - ); - } -} diff --git a/frontend/src/page/context/create.js b/frontend/src/page/context/create.js deleted file mode 100644 index f42ef571a4..0000000000 --- a/frontend/src/page/context/create.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import CreateContextField from '../../component/context/create-context-container'; -import PropTypes from 'prop-types'; - -const render = ({ history }) => ; - -render.propTypes = { - history: PropTypes.object.isRequired, -}; - -export default render; diff --git a/frontend/src/page/context/edit.js b/frontend/src/page/context/edit.js deleted file mode 100644 index bf3a02ed06..0000000000 --- a/frontend/src/page/context/edit.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import CreateContextField from '../../component/context/edit-context-container'; -import PropTypes from 'prop-types'; - -const render = ({ match: { params }, history }) => ( - -); - -render.propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -}; - -export default render; diff --git a/frontend/src/page/context/index.js b/frontend/src/page/context/index.js deleted file mode 100644 index 983f66ccfe..0000000000 --- a/frontend/src/page/context/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import ContextList from '../../component/context/ContextList'; -import PropTypes from 'prop-types'; - -const render = ({ history }) => ; - -render.propTypes = { - history: PropTypes.object.isRequired, -}; - -export default render; diff --git a/frontend/src/page/features/copy.js b/frontend/src/page/features/copy.js index 935c55fe38..7afe17a487 100644 --- a/frontend/src/page/features/copy.js +++ b/frontend/src/page/features/copy.js @@ -1,5 +1,5 @@ import React from 'react'; -import CopyFeatureToggleForm from '../../component/feature/create/CopyFeature'; +import CopyFeatureToggleForm from '../../component/feature/CopyFeature'; import PropTypes from 'prop-types'; const render = ({ history, match: { params } }) => ( diff --git a/frontend/src/page/features/redirect.js b/frontend/src/page/features/redirect.js deleted file mode 100644 index 9e123be7af..0000000000 --- a/frontend/src/page/features/redirect.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import RedirectFeatureView from '../../component/feature/RedirectFeatureView'; - -export default class RedirectFeatureViewPage extends PureComponent { - static propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, - }; - - render() { - const { - match: { params }, - history, - } = this.props; - return ( - - ); - } -} diff --git a/frontend/src/page/features/show.js b/frontend/src/page/features/show.js deleted file mode 100644 index 76f743f9d3..0000000000 --- a/frontend/src/page/features/show.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import FeatureView from '../../component/feature/FeatureView'; - -export default class Features extends PureComponent { - static propTypes = { - match: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, - }; - - render() { - const { - match: { params }, - history, - } = this.props; - return ( - - ); - } -} diff --git a/frontend/src/page/history/index.js b/frontend/src/page/history/index.js index a5f86a8c30..36c8a2f149 100644 --- a/frontend/src/page/history/index.js +++ b/frontend/src/page/history/index.js @@ -2,16 +2,16 @@ import { Alert } from '@material-ui/lab'; import React, { useContext } from 'react'; import { ADMIN } from '../../component/providers/AccessProvider/permissions'; import ConditionallyRender from '../../component/common/ConditionallyRender'; -import HistoryComponent from '../../component/history/EventHistory'; +import { EventHistory } from '../../component/history/EventHistory/EventHistory'; import AccessContext from '../../contexts/AccessContext'; -const HistoryPage = ({ history }) => { +const HistoryPage = () => { const { hasAccess } = useContext(AccessContext); return ( } + show={} elseShow={ You need instance admin to access this section. diff --git a/frontend/src/page/history/toggle.js b/frontend/src/page/history/toggle.jsx similarity index 55% rename from frontend/src/page/history/toggle.js rename to frontend/src/page/history/toggle.jsx index 428e298826..b89257af1c 100644 --- a/frontend/src/page/history/toggle.js +++ b/frontend/src/page/history/toggle.jsx @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import HistoryListToggle from '../../component/history/FeatureEventHistory'; +import { FeatureEventHistory } from '../../component/history/FeatureEventHistory/FeatureEventHistory'; const render = ({ match: { params } }) => ( - + ); render.propTypes = { diff --git a/frontend/src/setupTests.ts b/frontend/src/setupTests.ts index 80c5e96678..38f5024be9 100644 --- a/frontend/src/setupTests.ts +++ b/frontend/src/setupTests.ts @@ -1,6 +1,3 @@ import '@testing-library/jest-dom' -import { configure } from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; process.env.TZ = 'UTC'; -configure({ adapter: new Adapter() }); diff --git a/frontend/src/store/history/actions.js b/frontend/src/store/history/actions.js deleted file mode 100644 index efbb5f622b..0000000000 --- a/frontend/src/store/history/actions.js +++ /dev/null @@ -1,33 +0,0 @@ -import api from './api'; -import { dispatchError } from '../util'; - -export const RECEIVE_HISTORY = 'RECEIVE_HISTORY'; -export const ERROR_RECEIVE_HISTORY = 'ERROR_RECEIVE_HISTORY'; - -export const RECEIVE_HISTORY_FOR_TOGGLE = 'RECEIVE_HISTORY_FOR_TOGGLE'; - -const receiveHistory = json => ({ - type: RECEIVE_HISTORY, - value: json.events, -}); - -const receiveHistoryforToggle = json => ({ - type: RECEIVE_HISTORY_FOR_TOGGLE, - value: json, -}); - -export function fetchHistory() { - return dispatch => - api - .fetchAll() - .then(json => dispatch(receiveHistory(json))) - .catch(dispatchError(dispatch, ERROR_RECEIVE_HISTORY)); -} - -export function fetchHistoryForToggle(toggleName) { - return dispatch => - api - .fetchHistoryForToggle(toggleName) - .then(json => dispatch(receiveHistoryforToggle(json))) - .catch(dispatchError(dispatch, ERROR_RECEIVE_HISTORY)); -} diff --git a/frontend/src/store/history/api.js b/frontend/src/store/history/api.js deleted file mode 100644 index 8dfabe6f06..0000000000 --- a/frontend/src/store/history/api.js +++ /dev/null @@ -1,21 +0,0 @@ -import { formatApiPath } from '../../utils/format-path'; -import { throwIfNotSuccess } from '../api-helper'; - -const URI = formatApiPath('api/admin/events'); - -function fetchAll() { - return fetch(URI, { credentials: 'include' }) - .then(throwIfNotSuccess) - .then(response => response.json()); -} - -function fetchHistoryForToggle(toggleName) { - return fetch(`${URI}/${toggleName}`, { credentials: 'include' }) - .then(throwIfNotSuccess) - .then(response => response.json()); -} - -export default { - fetchAll, - fetchHistoryForToggle, -}; diff --git a/frontend/src/store/history/index.js b/frontend/src/store/history/index.js deleted file mode 100644 index b58488cc35..0000000000 --- a/frontend/src/store/history/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { List, Map as $Map } from 'immutable'; -import { RECEIVE_HISTORY, RECEIVE_HISTORY_FOR_TOGGLE } from './actions'; -import { USER_LOGOUT, USER_LOGIN } from '../user/actions'; - -function getInitState() { - return new $Map({ list: new List(), toggles: new $Map() }); -} - -const historyStore = (state = getInitState(), action) => { - switch (action.type) { - case RECEIVE_HISTORY_FOR_TOGGLE: - return state.setIn(['toggles', action.value.toggleName], new List(action.value.events)); - case RECEIVE_HISTORY: - return state.set('list', new List(action.value)); - case USER_LOGOUT: - case USER_LOGIN: - return getInitState(); - default: - return state; - } -}; - -export default historyStore; diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js index 70ecc0b250..b329b9bab4 100644 --- a/frontend/src/store/index.js +++ b/frontend/src/store/index.js @@ -6,7 +6,6 @@ import featureTags from './feature-tags'; import tagTypes from './tag-type'; import tags from './tag'; import strategies from './strategy'; -import history from './history'; // eslint-disable-line import archive from './archive'; import error from './error'; import settings from './settings'; @@ -29,7 +28,6 @@ const unleashStore = combineReducers({ tagTypes, tags, featureTags, - history, archive, error, settings, diff --git a/frontend/src/store/settings/index.js b/frontend/src/store/settings/index.js index a59ef68cbb..76ead4ed54 100644 --- a/frontend/src/store/settings/index.js +++ b/frontend/src/store/settings/index.js @@ -2,13 +2,13 @@ import { fromJS } from 'immutable'; import { UPDATE_SETTING } from './actions'; import { USER_LOGOUT, USER_LOGIN } from '../user/actions'; -import { getBasePath } from '../../utils/format-path'; +import { getBasePath } from '../../utils/format-path'; const localStorage = window.localStorage || { setItem: () => {}, getItem: () => {}, }; -const basePath =  getBasePath(); +const basePath = getBasePath(); const SETTINGS = `${basePath}:settings`; const DEFAULT = fromJS({ location: {} }); diff --git a/frontend/src/utils/get-strategy-object.ts b/frontend/src/utils/get-strategy-object.ts index ac16a3f939..b6df4a41ee 100644 --- a/frontend/src/utils/get-strategy-object.ts +++ b/frontend/src/utils/get-strategy-object.ts @@ -1,5 +1,5 @@ -import { resolveDefaultParamValue } from '../component/feature/strategy/AddStrategy/utils'; import { IStrategy, IParameter } from '../interfaces/strategy'; +import { resolveDefaultParamValue } from './resolve-default-param-value'; export const getStrategyObject = ( selectableStrategies: IStrategy[], diff --git a/frontend/src/component/feature/strategy/AddStrategy/utils.js b/frontend/src/utils/resolve-default-param-value.ts similarity index 100% rename from frontend/src/component/feature/strategy/AddStrategy/utils.js rename to frontend/src/utils/resolve-default-param-value.ts diff --git a/frontend/src/utils/route-path-helpers.ts b/frontend/src/utils/route-path-helpers.ts index 6b06119d0d..4bdf3102ff 100644 --- a/frontend/src/utils/route-path-helpers.ts +++ b/frontend/src/utils/route-path-helpers.ts @@ -1,5 +1,5 @@ -export const getTogglePath = (projectId: string, featureToggleName: string, newPath: boolean) => { - return `/projects/${projectId}/features${newPath ? `2/${featureToggleName}` : `/${featureToggleName}/strategies`}`; +export const getTogglePath = (projectId: string, featureToggleName: string) => { + return `/projects/${projectId}/features/${featureToggleName}`; }; export const getToggleCopyPath = ( diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 804eb4d36e..b8f8f65918 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1959,13 +1959,6 @@ dependencies: "@babel/types" "^7.3.0" -"@types/cheerio@*", "@types/cheerio@^0.22.22": - version "0.22.28" - resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.28.tgz" - integrity sha512-ehUMGSW5IeDxJjbru4awKYMlKGmo1wSSGUVqXtYwlgmUM8X1a0PZttEIm6yEY7vHsY/hh6iPnklF213G0UColw== - dependencies: - "@types/node" "*" - "@types/debounce@1.2.1": version "1.2.1" resolved "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz" @@ -1976,29 +1969,6 @@ resolved "https://registry.yarnpkg.com/@types/deep-diff/-/deep-diff-1.0.1.tgz#eae15119c68b72b541731872a2883da89dc39396" integrity sha512-cZIq2GFcPmW0/M7dtLuphyoU8f3zpTcBgV+wkFFJ0CK0lwRVGGLaBSJZ98qs4LjtLimPq1Bb2VJnhGn6SEE4IA== -"@types/enzyme-adapter-react-16@1.0.6": - version "1.0.6" - resolved "https://registry.npmjs.org/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.6.tgz" - integrity sha512-VonDkZ15jzqDWL8mPFIQnnLtjwebuL9YnDkqeCDYnB4IVgwUm0mwKkqhrxLL6mb05xm7qqa3IE95m8CZE9imCg== - dependencies: - "@types/enzyme" "*" - -"@types/enzyme@*": - version "3.10.8" - resolved "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.10.8.tgz" - integrity sha512-vlOuzqsTHxog6PV79+tvOHFb6hq4QZKMq1lLD9MaWD1oec2lHTKndn76XOpSwCA0oFTaIbKVPrgM3k78Jjd16g== - dependencies: - "@types/cheerio" "*" - "@types/react" "*" - -"@types/enzyme@3.10.11": - version "3.10.11" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.11.tgz#8924bd92cc63ac1843e215225dfa8f71555fe814" - integrity sha512-LEtC7zXsQlbGXWGcnnmOI7rTyP+i1QzQv4Va91RKXDEukLDaNyxu0rXlfMiGEhJwfgTPCTb0R+Pnlj//oM9e/w== - dependencies: - "@types/cheerio" "*" - "@types/react" "*" - "@types/eslint@^7.2.6": version "7.2.8" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.8.tgz" @@ -2159,6 +2129,13 @@ dependencies: "@types/react" "*" +"@types/react-outside-click-handler@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/react-outside-click-handler/-/react-outside-click-handler-1.3.1.tgz#e4772ba550e1a548468203194d2615d8f06acdf9" + integrity sha512-0BNan5zIIDyO5k9LFSG+60ZxQ/0wf+LTF9BJx3oOUdOaJlZk6RCe52jRB75mlvLLJx2YLa61+NidOwBfptWMKw== + dependencies: + "@types/react" "*" + "@types/react-redux@^7.1.20": version "7.1.20" resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz" @@ -2671,7 +2648,7 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^4.0.0" -airbnb-prop-types@^2.15.0, airbnb-prop-types@^2.16.0: +airbnb-prop-types@^2.15.0: version "2.16.0" resolved "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz" integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg== @@ -2856,11 +2833,6 @@ arr-union@^3.1.0: resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" @@ -3736,30 +3708,6 @@ check-types@^11.1.1: resolved "https://registry.npmjs.org/check-types/-/check-types-11.1.2.tgz" integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ== -cheerio-select-tmp@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz" - integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ== - dependencies: - css-select "^3.1.2" - css-what "^4.0.0" - domelementtype "^2.1.0" - domhandler "^4.0.0" - domutils "^2.4.4" - -cheerio@^1.0.0-rc.3: - version "1.0.0-rc.5" - resolved "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.5.tgz" - integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw== - dependencies: - cheerio-select-tmp "^0.1.0" - dom-serializer "~1.2.0" - domhandler "^4.0.0" - entities "~2.1.0" - htmlparser2 "^6.0.0" - parse5 "^6.0.0" - parse5-htmlparser2-tree-adapter "^6.0.0" - "chokidar@>=3.0.0 <4.0.0": version "3.5.2" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" @@ -4013,7 +3961,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.19.0, commander@^2.20.0: +commander@^2.20.0: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4399,17 +4347,6 @@ css-select@^2.0.0, css-select@^2.0.2: domutils "^1.7.0" nth-check "^1.0.2" -css-select@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/css-select/-/css-select-3.1.2.tgz" - integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA== - dependencies: - boolbase "^1.0.0" - css-what "^4.0.0" - domhandler "^4.0.0" - domutils "^2.4.3" - nth-check "^2.0.0" - css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" @@ -4439,11 +4376,6 @@ css-what@^3.2.1: resolved "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== -css-what@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/css-what/-/css-what-4.0.0.tgz" - integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== - css.escape@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz" @@ -4879,11 +4811,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -discontinuous-range@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz" - integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= - dnd-core@14.0.1: version "14.0.1" resolved "https://registry.npmjs.org/dnd-core/-/dnd-core-14.0.1.tgz" @@ -4970,15 +4897,6 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@^1.0.1, dom-serializer@~1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz" - integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - entities "^2.0.0" - domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz" @@ -4989,7 +4907,7 @@ domelementtype@1, domelementtype@^1.3.1: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@^2.0.1, domelementtype@^2.1.0, domelementtype@^2.2.0: +domelementtype@^2.0.1: version "2.2.0" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== @@ -5008,13 +4926,6 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domhandler@^4.0.0, domhandler@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.1.0.tgz" - integrity sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ== - dependencies: - domelementtype "^2.2.0" - domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" @@ -5023,15 +4934,6 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.4.3, domutils@^2.4.4: - version "2.5.1" - resolved "https://registry.npmjs.org/domutils/-/domutils-2.5.1.tgz" - integrity sha512-hO1XwHMGAthA/1KL7c83oip/6UWo3FlUNIuWiWKltoiQ5oCOiqths8KknvY2jpOohUoUgnwa/+Rm7UpwpSbY/Q== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.1.0" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" @@ -5176,84 +5078,6 @@ entities@^2.0.0: resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -entities@~2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz" - integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== - -enzyme-adapter-react-16@1.15.6: - version "1.15.6" - resolved "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz" - integrity sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g== - dependencies: - enzyme-adapter-utils "^1.14.0" - enzyme-shallow-equal "^1.0.4" - has "^1.0.3" - object.assign "^4.1.2" - object.values "^1.1.2" - prop-types "^15.7.2" - react-is "^16.13.1" - react-test-renderer "^16.0.0-0" - semver "^5.7.0" - -enzyme-adapter-utils@^1.14.0: - version "1.14.0" - resolved "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz" - integrity sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg== - dependencies: - airbnb-prop-types "^2.16.0" - function.prototype.name "^1.1.3" - has "^1.0.3" - object.assign "^4.1.2" - object.fromentries "^2.0.3" - prop-types "^15.7.2" - semver "^5.7.1" - -enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz" - integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q== - dependencies: - has "^1.0.3" - object-is "^1.1.2" - -enzyme-to-json@3.6.2: - version "3.6.2" - resolved "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.6.2.tgz" - integrity sha512-Ynm6Z6R6iwQ0g2g1YToz6DWhxVnt8Dy1ijR2zynRKxTyBGA8rCDXU3rs2Qc4OKvUvc2Qoe1bcFK6bnPs20TrTg== - dependencies: - "@types/cheerio" "^0.22.22" - lodash "^4.17.21" - react-is "^16.12.0" - -enzyme@3.11.0: - version "3.11.0" - resolved "https://registry.npmjs.org/enzyme/-/enzyme-3.11.0.tgz" - integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw== - dependencies: - array.prototype.flat "^1.2.3" - cheerio "^1.0.0-rc.3" - enzyme-shallow-equal "^1.0.1" - function.prototype.name "^1.1.2" - has "^1.0.3" - html-element-map "^1.2.0" - is-boolean-object "^1.0.1" - is-callable "^1.1.5" - is-number-object "^1.0.4" - is-regex "^1.0.5" - is-string "^1.0.5" - is-subset "^0.1.1" - lodash.escape "^4.0.1" - lodash.isequal "^4.5.0" - object-inspect "^1.7.0" - object-is "^1.0.2" - object.assign "^4.1.0" - object.entries "^1.1.1" - object.values "^1.1.1" - raf "^3.4.1" - rst-selector-parser "^2.2.3" - string.prototype.trim "^1.2.1" - errno@^0.1.3, errno@~0.1.7: version "0.1.8" resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" @@ -6150,7 +5974,7 @@ function-bind@^1.1.1: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.2, function.prototype.name@^1.1.3: +function.prototype.name@^1.1.2: version "1.1.4" resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.4.tgz" integrity sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ== @@ -6530,14 +6354,6 @@ html-comment-regex@^1.1.0: resolved "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== -html-element-map@^1.2.0: - version "1.3.0" - resolved "https://registry.npmjs.org/html-element-map/-/html-element-map-1.3.0.tgz" - integrity sha512-AqCt/m9YaiMwaaAyOPdq4Ga0cM+jdDWWGueUMkdROZcTeClaGpN0AQeyGchZhTegQoABmc6+IqH7oCR/8vhQYg== - dependencies: - array-filter "^1.0.0" - call-bind "^1.0.2" - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" @@ -6595,16 +6411,6 @@ htmlparser2@^3.10.1: inherits "^2.0.1" readable-stream "^3.1.1" -htmlparser2@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.0.1.tgz" - integrity sha512-GDKPd+vk4jvSuvCbyuzx/unmXkk090Azec7LovXP8as1Hn8q9p3hbjmDGbUqqhknw0ajwit6LiiWqfiTUPMK7w== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.4.4" - entities "^2.0.0" - http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" @@ -6955,7 +6761,7 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.0.1, is-boolean-object@^1.1.0: +is-boolean-object@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz" integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== @@ -6967,7 +6773,7 @@ is-buffer@^1.1.5: resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.3: +is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== @@ -7194,7 +7000,7 @@ is-potential-custom-element-name@^1.0.0: resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz" integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= -is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.2: +is-regex@^1.0.4, is-regex@^1.1.0, is-regex@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz" integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== @@ -8203,21 +8009,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.escape@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz" - integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= - lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - lodash.flow@3.5.0: version "3.5.0" resolved "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz" @@ -8619,11 +8415,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -moo@^0.5.0: - version "0.5.1" - resolved "https://registry.npmjs.org/moo/-/moo-0.5.1.tgz" - integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz" @@ -8723,16 +8514,6 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -nearley@^2.7.10: - version "2.20.1" - resolved "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz" - integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== - dependencies: - commander "^2.19.0" - moo "^0.5.0" - railroad-diagrams "^1.0.0" - randexp "0.4.6" - negotiator@0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz" @@ -8892,13 +8673,6 @@ nth-check@^1.0.2: dependencies: boolbase "~1.0.0" -nth-check@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.0.0.tgz" - integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== - dependencies: - boolbase "^1.0.0" - num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz" @@ -8928,12 +8702,12 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.7.0, object-inspect@^1.9.0: +object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== -object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2: +object-is@^1.0.1, object-is@^1.1.2: version "1.1.5" resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== @@ -8963,7 +8737,7 @@ object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.3: +object.entries@^1.1.0, object.entries@^1.1.2, object.entries@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.3.tgz" integrity sha512-ym7h7OZebNS96hn5IJeyUmaWhaSM4SVtAPPfNLQEI2MYWCO2egsITb9nab2+i/Pwibx+R0mtn+ltKJXRSeTMGg== @@ -8973,7 +8747,7 @@ object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.2, object.entr es-abstract "^1.18.0-next.1" has "^1.0.3" -object.fromentries@^2.0.3, object.fromentries@^2.0.4: +object.fromentries@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.4.tgz" integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ== @@ -8999,7 +8773,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.2, object.values@^1.1.3: +object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.3.tgz" integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw== @@ -9250,14 +9024,7 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse5-htmlparser2-tree-adapter@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz" - integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== - dependencies: - parse5 "^6.0.1" - -parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: +parse5@6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -10295,6 +10062,15 @@ prop-types@^15.6.2, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz" @@ -10425,24 +10201,11 @@ raf@^3.4.1: dependencies: performance-now "^2.1.0" -railroad-diagrams@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz" - integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= - ramda@~0.27.1: version "0.27.1" resolved "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz" integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== -randexp@0.4.6: - version "0.4.6" - resolved "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz" - integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== - dependencies: - discontinuous-range "1.0.0" - ret "~0.1.10" - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -10547,7 +10310,7 @@ react-error-overlay@^6.0.9: resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -10680,9 +10443,9 @@ react-scripts@4.0.3: optionalDependencies: fsevents "^2.1.3" -react-test-renderer@^16.0.0-0: +react-test-renderer@^16.14.0: version "16.14.0" - resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae" integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg== dependencies: object-assign "^4.1.1" @@ -11214,14 +10977,6 @@ rollup@^1.31.1: "@types/node" "*" acorn "^7.1.0" -rst-selector-parser@^2.2.3: - version "2.2.3" - resolved "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz" - integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= - dependencies: - lodash.flattendeep "^4.4.0" - nearley "^2.7.10" - rsvp@^4.8.4: version "4.8.5" resolved "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz" @@ -11324,7 +11079,7 @@ saxes@^5.0.1: scheduler@^0.19.1: version "0.19.1" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== dependencies: loose-envify "^1.1.0" @@ -11377,7 +11132,7 @@ selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -11917,15 +11672,6 @@ string.prototype.matchall@^4.0.4: regexp.prototype.flags "^1.3.1" side-channel "^1.0.4" -string.prototype.trim@^1.2.1: - version "1.2.4" - resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz" - integrity sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz"