diff --git a/frontend/src/component/application/ApplicationList/ApplicationList.tsx b/frontend/src/component/application/ApplicationList/ApplicationList.tsx
index db6c5416f3..972743e70c 100644
--- a/frontend/src/component/application/ApplicationList/ApplicationList.tsx
+++ b/frontend/src/component/application/ApplicationList/ApplicationList.tsx
@@ -1,9 +1,8 @@
import { useMemo, useState } from 'react';
import { CircularProgress } from '@material-ui/core';
import { Warning } from '@material-ui/icons';
-
import { AppsLinkList, styles as commonStyles } from '../../common';
-import SearchField from '../../common/SearchField/SearchField';
+import { SearchField } from 'component/common/SearchField/SearchField';
import PageContent from '../../common/PageContent/PageContent';
import HeaderTitle from '../../common/HeaderTitle';
import useApplications from '../../../hooks/api/getters/useApplications/useApplications';
diff --git a/frontend/src/component/common/SearchField/SearchField.jsx b/frontend/src/component/common/SearchField/SearchField.jsx
deleted file mode 100644
index 87f7980e69..0000000000
--- a/frontend/src/component/common/SearchField/SearchField.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import React, { useState } from 'react';
-import classnames from 'classnames';
-import PropTypes from 'prop-types';
-import { debounce } from 'debounce';
-import { InputBase } from '@material-ui/core';
-import SearchIcon from '@material-ui/icons/Search';
-
-import { useStyles } from './styles';
-
-function SearchField({ initialValue = '', updateValue, className = '' }) {
- const styles = useStyles();
-
- const [localValue, setLocalValue] = useState(initialValue);
- const debounceUpdateValue = debounce(updateValue, 500);
-
- const handleChange = e => {
- e.preventDefault();
- const v = e.target.value || '';
- setLocalValue(v);
- debounceUpdateValue(v);
- };
-
- const handleKeyPress = e => {
- if (e.key === 'Enter') {
- updateValue(localValue);
- }
- };
-
- const updateNow = () => {
- updateValue(localValue);
- };
-
- return (
-
- );
-}
-
-SearchField.propTypes = {
- value: PropTypes.string,
- updateValue: PropTypes.func.isRequired,
-};
-
-export default SearchField;
diff --git a/frontend/src/component/common/SearchField/SearchField.tsx b/frontend/src/component/common/SearchField/SearchField.tsx
new file mode 100644
index 0000000000..333cc5a603
--- /dev/null
+++ b/frontend/src/component/common/SearchField/SearchField.tsx
@@ -0,0 +1,74 @@
+import React, { useState } from 'react';
+import classnames from 'classnames';
+import { debounce } from 'debounce';
+import { InputBase, Chip } from '@material-ui/core';
+import SearchIcon from '@material-ui/icons/Search';
+import { useStyles } from './styles';
+import ConditionallyRender from 'component/common/ConditionallyRender';
+
+interface ISearchFieldProps {
+ updateValue: React.Dispatch>;
+ initialValue?: string;
+ className?: string;
+ showValueChip?: boolean;
+}
+
+export const SearchField = ({
+ updateValue,
+ initialValue = '',
+ className = '',
+ showValueChip,
+}: ISearchFieldProps) => {
+ const styles = useStyles();
+ const [localValue, setLocalValue] = useState(initialValue);
+ const debounceUpdateValue = debounce(updateValue, 500);
+
+ const handleChange = (event: React.ChangeEvent) => {
+ event.preventDefault();
+ const value = event.target.value || '';
+ setLocalValue(value);
+ debounceUpdateValue(value);
+ };
+
+ const handleKeyPress = (event: React.KeyboardEvent) => {
+ if (event.key === 'Enter') {
+ updateValue(localValue);
+ }
+ };
+
+ const updateNow = () => {
+ updateValue(localValue);
+ };
+
+ const onDelete = () => {
+ setLocalValue('');
+ updateValue('');
+ };
+
+ return (
+
+ );
+};
diff --git a/frontend/src/component/common/SearchField/styles.js b/frontend/src/component/common/SearchField/styles.js
index 09a4e41d1f..ed7c9e480a 100644
--- a/frontend/src/component/common/SearchField/styles.js
+++ b/frontend/src/component/common/SearchField/styles.js
@@ -1,6 +1,12 @@
import { makeStyles } from '@material-ui/styles';
export const useStyles = makeStyles(theme => ({
+ container: {
+ display: 'flex',
+ alignItems: 'center',
+ flexWrap: 'wrap',
+ gap: '1rem',
+ },
search: {
display: 'flex',
alignItems: 'center',
@@ -8,9 +14,6 @@ export const useStyles = makeStyles(theme => ({
borderRadius: '25px',
padding: '0.25rem 0.5rem',
maxWidth: '450px',
- [theme.breakpoints.down('sm')]: {
- margin: '0 auto',
- },
[theme.breakpoints.down('xs')]: {
width: '100%',
},
diff --git a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx
index 7c5ea29409..a356fe3015 100644
--- a/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx
+++ b/frontend/src/component/feature/FeatureToggleList/FeatureToggleList.jsx
@@ -5,20 +5,15 @@ import { Link } from 'react-router-dom';
import { Button, IconButton, List, ListItem, Tooltip } from '@material-ui/core';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import { Add } from '@material-ui/icons';
-
import FeatureToggleListItem from './FeatureToggleListItem';
-import SearchField from '../../common/SearchField/SearchField';
+import { SearchField } from '../../common/SearchField/SearchField';
import FeatureToggleListActions from './FeatureToggleListActions';
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
import PageContent from '../../common/PageContent/PageContent';
import HeaderTitle from '../../common/HeaderTitle';
-
import loadingFeatures from './loadingFeatures';
-
import { CREATE_FEATURE } from '../../providers/AccessProvider/permissions';
-
import AccessContext from '../../../contexts/AccessContext';
-
import { useStyles } from './styles';
import ListPlaceholder from '../../common/ListPlaceholder/ListPlaceholder';
import { getCreateTogglePath } from '../../../utils/route-path-helpers';
@@ -101,7 +96,11 @@ const FeatureToggleList = ({
);
};
- const headerTitle = archive ? 'Archived Features' : 'Features';
+ const headerTitle = filter.query
+ ? 'Search results'
+ : archive
+ ? 'Archived Features'
+ : 'Features';
return (
@@ -109,6 +108,7 @@ const FeatureToggleList = ({
-
+
@@ -31,7 +33,7 @@ exports[`renders correctly with one feature 1`] = `
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
- placeholder="Search…"
+ placeholder="Search..."
type="text"
value=""
/>
@@ -55,29 +57,29 @@ exports[`renders correctly with one feature 1`] = `
}
>
Features
-
+
@@ -223,7 +227,7 @@ exports[`renders correctly with one feature without permissions 1`] = `
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
- placeholder="Search…"
+ placeholder="Search..."
type="text"
value=""
/>
@@ -247,29 +251,29 @@ exports[`renders correctly with one feature without permissions 1`] = `
}
>
Features
({
searchBarContainer: {
marginBottom: '2rem',
display: 'flex',
+ gap: '1rem',
justifyContent: 'space-between',
alignItems: 'center',
[theme.breakpoints.down('xs')]: {
diff --git a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx
index 38fc7d70a6..0c8afb086c 100644
--- a/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureStrategies/FeatureStrategiesEnvironments/FeatureStrategyEditable/FeatureStrategyEditable.tsx
@@ -15,7 +15,6 @@ import { Tooltip } from '@material-ui/core';
import ConditionallyRender from '../../../../../common/ConditionallyRender';
import { useStyles } from './FeatureStrategyEditable.styles';
import { Delete } from '@material-ui/icons';
-import { PRODUCTION } from '../../../../../../constants/environmentTypes';
import {
DELETE_STRATEGY_ID,
STRATEGY_ACCORDION_ID,
diff --git a/frontend/src/hooks/useEventSettings.ts b/frontend/src/hooks/useEventSettings.ts
index 21478a8bc6..d7c7c2ea95 100644
--- a/frontend/src/hooks/useEventSettings.ts
+++ b/frontend/src/hooks/useEventSettings.ts
@@ -1,5 +1,5 @@
import { getBasePath } from '../utils/format-path';
-import { createPersistentGlobalState } from './usePersistentGlobalState';
+import { createPersistentGlobalStateHook } from './usePersistentGlobalState';
import React from 'react';
export interface IEventSettings {
@@ -21,7 +21,7 @@ const createInitialValue = (): IEventSettings => {
return { showData: false };
};
-const useGlobalState = createPersistentGlobalState(
+const useGlobalState = createPersistentGlobalStateHook(
`${getBasePath()}:useEventSettings:v1`,
createInitialValue()
);
diff --git a/frontend/src/hooks/useFeaturesFilter.ts b/frontend/src/hooks/useFeaturesFilter.ts
index 452f73911a..085d13622b 100644
--- a/frontend/src/hooks/useFeaturesFilter.ts
+++ b/frontend/src/hooks/useFeaturesFilter.ts
@@ -1,7 +1,6 @@
-import { IFeatureToggle } from '../interfaces/featureToggle';
+import { IFeatureToggle } from 'interfaces/featureToggle';
import React, { useMemo } from 'react';
-import { getBasePath } from '../utils/format-path';
-import { createPersistentGlobalState } from './usePersistentGlobalState';
+import { createGlobalStateHook } from 'hooks/useGlobalState';
export interface IFeaturesFilter {
query?: string;
@@ -16,8 +15,8 @@ export interface IFeaturesSortOutput {
// Store the features filter state globally, and in localStorage.
// When changing the format of IFeaturesFilter, change the version as well.
-const useFeaturesFilterState = createPersistentGlobalState(
- `${getBasePath()}:useFeaturesFilter:v1`,
+const useFeaturesFilterState = createGlobalStateHook(
+ 'useFeaturesFilterState',
{ project: '*' }
);
diff --git a/frontend/src/hooks/useFeaturesSort.ts b/frontend/src/hooks/useFeaturesSort.ts
index 282916671a..a72c2fc295 100644
--- a/frontend/src/hooks/useFeaturesSort.ts
+++ b/frontend/src/hooks/useFeaturesSort.ts
@@ -1,7 +1,7 @@
import { IFeatureToggle } from '../interfaces/featureToggle';
import React, { useMemo } from 'react';
import { getBasePath } from '../utils/format-path';
-import { createPersistentGlobalState } from './usePersistentGlobalState';
+import { createPersistentGlobalStateHook } from './usePersistentGlobalState';
type FeaturesSortType =
| 'name'
@@ -29,7 +29,7 @@ export interface IFeaturesFilterSortOption {
// Store the features sort state globally, and in localStorage.
// When changing the format of IFeaturesSort, change the version as well.
-const useFeaturesSortState = createPersistentGlobalState(
+const useFeaturesSortState = createPersistentGlobalStateHook(
`${getBasePath()}:useFeaturesSort:v1`,
{ type: 'name' }
);
diff --git a/frontend/src/hooks/useGlobalState.ts b/frontend/src/hooks/useGlobalState.ts
new file mode 100644
index 0000000000..37469ebd6a
--- /dev/null
+++ b/frontend/src/hooks/useGlobalState.ts
@@ -0,0 +1,23 @@
+import React from 'react';
+import { createGlobalState } from 'react-hooks-global-state';
+
+type UseGlobalState = () => [
+ value: T,
+ setValue: React.Dispatch>
+];
+
+// Create a hook that stores global state (shared across all hook instances).
+export const createGlobalStateHook = (
+ key: string,
+ initialValue: T
+): UseGlobalState => {
+ const container = createGlobalState<{ [key: string]: T }>({
+ [key]: initialValue,
+ });
+
+ const setGlobalState = (value: React.SetStateAction) => {
+ container.setGlobalState(key, value);
+ };
+
+ return () => [container.useGlobalState(key)[0], setGlobalState];
+};
diff --git a/frontend/src/hooks/useLocationSettings.ts b/frontend/src/hooks/useLocationSettings.ts
index 5ffa2d18de..5e74ff0240 100644
--- a/frontend/src/hooks/useLocationSettings.ts
+++ b/frontend/src/hooks/useLocationSettings.ts
@@ -1,5 +1,5 @@
import { getBasePath } from '../utils/format-path';
-import { createPersistentGlobalState } from './usePersistentGlobalState';
+import { createPersistentGlobalStateHook } from './usePersistentGlobalState';
import React from 'react';
export interface ILocationSettings {
@@ -23,7 +23,7 @@ const createInitialValue = (): ILocationSettings => {
return { locale: navigator.language };
};
-const useGlobalState = createPersistentGlobalState(
+const useGlobalState = createPersistentGlobalStateHook(
`${getBasePath()}:useLocationSettings:v1`,
createInitialValue()
);
diff --git a/frontend/src/hooks/usePersistentGlobalState.ts b/frontend/src/hooks/usePersistentGlobalState.ts
index 1a06da3ffe..1f743a23ea 100644
--- a/frontend/src/hooks/usePersistentGlobalState.ts
+++ b/frontend/src/hooks/usePersistentGlobalState.ts
@@ -10,7 +10,7 @@ type UsePersistentGlobalState = () => [
// Create a hook that stores global state (shared across all hook instances).
// The state is also persisted to localStorage and restored on page load.
// The localStorage state is not synced between tabs.
-export const createPersistentGlobalState = (
+export const createPersistentGlobalStateHook = (
key: string,
initialValue: T
): UsePersistentGlobalState => {