1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

Fix/cleanup (#276)

* chore: update changelog

* 4.0.0-alpha.10

* fix: add dnd provider as top level wrapper

* fix: add forgotten password link to password auth

* fix: remove unecceesary dnd component

* fix: check for usedTokenError
This commit is contained in:
Fredrik Strand Oseberg 2021-04-27 09:48:00 +02:00 committed by GitHub
parent e81918ee48
commit 1f46d3b21b
7 changed files with 134 additions and 120 deletions

View File

@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
The latest version of this document is always available in The latest version of this document is always available in
[releases][releases-url]. [releases][releases-url].
# 4.0.0-alpha.10
- fix: password
# 4.0.0-alpha.9 # 4.0.0-alpha.9
- fix: optimizations - fix: optimizations
- feat: user profile - feat: user profile

View File

@ -1,7 +1,7 @@
{ {
"name": "unleash-frontend", "name": "unleash-frontend",
"description": "unleash your features", "description": "unleash your features",
"version": "4.0.0-alpha.9", "version": "4.0.0-alpha.10",
"keywords": [ "keywords": [
"unleash", "unleash",
"feature toggle", "feature toggle",

View File

@ -1,15 +0,0 @@
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import React from 'react';
function useDNDProviderElement(props) {
if (!props.children) return null;
return <DndProvider backend={HTML5Backend}>{props.children}</DndProvider>;
}
export default function DragAndDrop(props) {
const DNDElement = useDNDProviderElement(props);
return <React.Fragment>{DNDElement}</React.Fragment>;
}

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import cloneDeep from 'lodash.clonedeep'; import cloneDeep from 'lodash.clonedeep';
@ -6,7 +6,6 @@ import arrayMove from 'array-move';
import { Button } from '@material-ui/core'; import { Button } from '@material-ui/core';
import { Alert } from '@material-ui/lab'; import { Alert } from '@material-ui/lab';
import DragAndDrop from '../../common/drag-and-drop';
import HeaderTitle from '../../common/HeaderTitle'; import HeaderTitle from '../../common/HeaderTitle';
import { updateIndexInArray } from '../../common/util'; import { updateIndexInArray } from '../../common/util';
import styles from './strategy.module.scss'; import styles from './strategy.module.scss';
@ -27,7 +26,9 @@ const StrategiesList = props => {
const [delStrategy, setDelStrategy] = useState(null); const [delStrategy, setDelStrategy] = useState(null);
const [showCreateStrategy, setShowCreateStrategy] = useState(false); const [showCreateStrategy, setShowCreateStrategy] = useState(false);
const [showAlert, setShowAlert] = useState(true); const [showAlert, setShowAlert] = useState(true);
const [editableStrategies, updateEditableStrategies] = useState(cloneDeep(props.configuredStrategies)); const [editableStrategies, updateEditableStrategies] = useState(
cloneDeep(props.configuredStrategies)
);
const [editStrategyIndex, setEditStrategyIndex] = useState(); const [editStrategyIndex, setEditStrategyIndex] = useState();
useEffect(() => { useEffect(() => {
@ -39,7 +40,11 @@ const StrategiesList = props => {
const updateStrategy = index => strategy => { const updateStrategy = index => strategy => {
const newStrategy = { ...strategy }; const newStrategy = { ...strategy };
const newStrategies = updateIndexInArray(editableStrategies, index, newStrategy); const newStrategies = updateIndexInArray(
editableStrategies,
index,
newStrategy
);
updateEditableStrategies(newStrategies); updateEditableStrategies(newStrategies);
}; };
@ -87,9 +92,13 @@ const StrategiesList = props => {
const strategy = editableStrategies[delStrategy]; const strategy = editableStrategies[delStrategy];
if (!strategy.new) { if (!strategy.new) {
await props.saveStrategies(props.configuredStrategies.filter((_, i) => i !== delStrategy)); await props.saveStrategies(
props.configuredStrategies.filter((_, i) => i !== delStrategy)
);
} }
updateEditableStrategies(editableStrategies.filter((_, i) => i !== delStrategy)); updateEditableStrategies(
editableStrategies.filter((_, i) => i !== delStrategy)
);
setDelStrategy(undefined); setDelStrategy(undefined);
setShowDelDialog(null); setShowDelDialog(null);
@ -100,7 +109,12 @@ const StrategiesList = props => {
setEditStrategyIndex(undefined); setEditStrategyIndex(undefined);
}; };
const { strategies, configuredStrategies, featureToggleName, editable } = props; const {
strategies,
configuredStrategies,
featureToggleName,
editable,
} = props;
const resolveStrategyDefinition = strategyName => { const resolveStrategyDefinition = strategyName => {
if (!strategies || strategies.length === 0) { if (!strategies || strategies.length === 0) {
@ -139,11 +153,12 @@ const StrategiesList = props => {
strategy={editingStrategy} strategy={editingStrategy}
updateStrategy={updateStrategy(editStrategyIndex)} updateStrategy={updateStrategy(editStrategyIndex)}
saveStrategy={saveStrategy(editStrategyIndex)} saveStrategy={saveStrategy(editStrategyIndex)}
strategyDefinition={resolveStrategyDefinition(editingStrategy.name)} strategyDefinition={resolveStrategyDefinition(
editingStrategy.name
)}
onCancel={clearAll} onCancel={clearAll}
/> />
) : null} ) : null}
<DragAndDrop>
<ConditionallyRender <ConditionallyRender
condition={editable} condition={editable}
show={ show={
@ -167,18 +182,28 @@ const StrategiesList = props => {
<ConditionallyRender <ConditionallyRender
condition={showAlert} condition={showAlert}
show={ show={
<Alert severity="info" className={styles.infoCard} onClose={() => setShowAlert(false)}> <Alert
Strategies allow you fine grained control over how to activate your features, and are severity="info"
composable blocks that are executed in an OR fashion. As an example, you can have a gradual className={styles.infoCard}
rollout that targets 80% of users in a region of the world (using the enterprise feature of onClose={() => setShowAlert(false)}
constraints), and another gradual rollout that targets 20% of the users in another region. >
If you don't add a strategy, the default strategy is activated which means that the feature Strategies allow you fine grained control over how to
will be strictly on/off for your entire userbase. activate your features, and are composable blocks that
are executed in an OR fashion. As an example, you can
have a gradual rollout that targets 80% of users in a
region of the world (using the enterprise feature of
constraints), and another gradual rollout that targets
20% of the users in another region. If you don't add a
strategy, the default strategy is activated which means
that the feature will be strictly on/off for your entire
userbase.
</Alert> </Alert>
} }
/> />
<ConditionallyRender <ConditionallyRender
condition={!configuredStrategies || configuredStrategies.length === 0} condition={
!configuredStrategies || configuredStrategies.length === 0
}
show={ show={
<p style={{ padding: '0 16px' }}> <p style={{ padding: '0 16px' }}>
<i>No activation strategies selected.</i> <i>No activation strategies selected.</i>
@ -199,7 +224,6 @@ const StrategiesList = props => {
condition={cards.length > 0} condition={cards.length > 0}
show={<div className={styles.strategyListCards}>{cards}</div>} show={<div className={styles.strategyListCards}>{cards}</div>}
/> />
</DragAndDrop>
</div> </div>
); );
}; };

View File

@ -1,16 +1,12 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import classnames from 'classnames'; import classnames from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import { Button, TextField, Typography, IconButton } from '@material-ui/core';
Button,
TextField,
Typography,
IconButton,
} from '@material-ui/core';
import ConditionallyRender from '../../common/ConditionallyRender'; import ConditionallyRender from '../../common/ConditionallyRender';
import { useHistory } from 'react-router'; import { useHistory } from 'react-router';
import { useCommonStyles } from '../../../common.styles'; import { useCommonStyles } from '../../../common.styles';
import { useStyles } from './PasswordAuth.styles'; import { useStyles } from './PasswordAuth.styles';
import { Link } from 'react-router-dom';
const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => { const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
const commonStyles = useCommonStyles(); const commonStyles = useCommonStyles();
@ -76,11 +72,7 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
const { usernameError, passwordError, apiError } = errors; const { usernameError, passwordError, apiError } = errors;
return ( return (
<form <form onSubmit={handleSubmit} action={authDetails.path}>
onSubmit={handleSubmit}
action={authDetails.path}
>
<Typography variant="subtitle2" className={styles.apiError}> <Typography variant="subtitle2" className={styles.apiError}>
{apiError} {apiError}
</Typography> </Typography>
@ -113,6 +105,11 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
size="small" size="small"
/> />
<Link to="/forgotten-password">
<Typography variant="body2">
Forgot your password?
</Typography>
</Link>
<Button <Button
variant="contained" variant="contained"
color="primary" color="primary"
@ -129,10 +126,13 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
const renderWithOptions = options => ( const renderWithOptions = options => (
<div> <div>
{options.map(o => ( {options.map(o => (
<div key={o.type} className={classnames( <div
key={o.type}
className={classnames(
styles.contentContainer, styles.contentContainer,
commonStyles.contentSpacingY commonStyles.contentSpacingY
)}> )}
>
<Button color="primary" variant="contained" href={o.path}> <Button color="primary" variant="contained" href={o.path}>
{o.value} {o.value}
</Button> </Button>
@ -155,15 +155,12 @@ const PasswordAuth = ({ authDetails, passwordLogin, loadInitialData }) => {
return ( return (
<div> <div>
<Typography variant="subtitle1"> <Typography variant="subtitle1">{authDetails.message}</Typography>
{authDetails.message}
</Typography>
<ConditionallyRender <ConditionallyRender
condition={options.length > 0} condition={options.length > 0}
show={renderWithOptions(options)} show={renderWithOptions(options)}
elseShow={renderLoginForm()} elseShow={renderLoginForm()}
/> />
</div> </div>
); );
}; };

View File

@ -8,6 +8,7 @@ const getFetcher = (token: string) => () =>
}).then(res => res.json()); }).then(res => res.json());
const INVALID_TOKEN_ERROR = 'InvalidTokenError'; const INVALID_TOKEN_ERROR = 'InvalidTokenError';
const USED_TOKEN_ERROR = 'UsedTokenError';
const useResetPassword = () => { const useResetPassword = () => {
const query = useQueryParams(); const query = useQueryParams();
@ -30,7 +31,9 @@ const useResetPassword = () => {
setLoading(!error && !data); setLoading(!error && !data);
}, [data, error]); }, [data, error]);
const invalidToken = !loading && data?.name === INVALID_TOKEN_ERROR; const invalidToken =
(!loading && data?.name === INVALID_TOKEN_ERROR) ||
data?.name === USED_TOKEN_ERROR;
return { token, data, error, loading, setLoading, invalidToken, retry }; return { token, data, error, loading, setLoading, invalidToken, retry };
}; };

View File

@ -8,6 +8,8 @@ import { Provider } from 'react-redux';
import { ThemeProvider, CssBaseline } from '@material-ui/core'; import { ThemeProvider, CssBaseline } from '@material-ui/core';
import thunkMiddleware from 'redux-thunk'; import thunkMiddleware from 'redux-thunk';
import { createStore, applyMiddleware, compose } from 'redux'; import { createStore, applyMiddleware, compose } from 'redux';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { StylesProvider } from '@material-ui/core/styles'; import { StylesProvider } from '@material-ui/core/styles';
import mainTheme from './themes/main-theme'; import mainTheme from './themes/main-theme';
@ -39,6 +41,7 @@ metricsPoller.start();
ReactDOM.render( ReactDOM.render(
<Provider store={unleashStore}> <Provider store={unleashStore}>
<DndProvider backend={HTML5Backend}>
<AccessProvider store={unleashStore}> <AccessProvider store={unleashStore}>
<HashRouter> <HashRouter>
<ThemeProvider theme={mainTheme}> <ThemeProvider theme={mainTheme}>
@ -51,6 +54,7 @@ ReactDOM.render(
</ThemeProvider> </ThemeProvider>
</HashRouter> </HashRouter>
</AccessProvider> </AccessProvider>
</DndProvider>
</Provider>, </Provider>,
document.getElementById('app') document.getElementById('app')
); );