mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-18 01:18:23 +02:00
feature: Add support for permission system in unleash frontend
refactored PermissionComponent to pure component fixed feature-list-item-component
This commit is contained in:
parent
1eb8fc0464
commit
aad612d3d6
@ -1,47 +1,21 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { ADMIN } from '../../permissions';
|
import { ADMIN } from '../../permissions';
|
||||||
|
|
||||||
class PermissionComponent extends Component {
|
const PermissionComponent = ({ user, permission, component, otherwise }) => {
|
||||||
static propTypes = {
|
if (
|
||||||
|
user &&
|
||||||
|
(!user.permissions || user.permissions.indexOf(ADMIN) !== -1 || user.permissions.indexOf(permission) !== -1)
|
||||||
|
) {
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
return otherwise || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
PermissionComponent.propTypes = {
|
||||||
user: PropTypes.object,
|
user: PropTypes.object,
|
||||||
component: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
|
component: PropTypes.node,
|
||||||
others: PropTypes.object,
|
|
||||||
denied: PropTypes.object,
|
|
||||||
granted: PropTypes.object,
|
|
||||||
otherwise: PropTypes.node,
|
otherwise: PropTypes.node,
|
||||||
permission: PropTypes.string,
|
permission: PropTypes.string,
|
||||||
children: PropTypes.node,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { user, otherwise, component: Component, permission, granted, denied, children, ...others } = this.props;
|
|
||||||
let grantedComponent = Component;
|
|
||||||
let deniedCompoinent = otherwise || '';
|
|
||||||
|
|
||||||
if (granted || denied) {
|
|
||||||
grantedComponent = (
|
|
||||||
<Component {...others} {...granted || {}}>
|
|
||||||
{children}
|
|
||||||
</Component>
|
|
||||||
);
|
|
||||||
deniedCompoinent = (
|
|
||||||
<Component {...others} {...denied || {}}>
|
|
||||||
{children}
|
|
||||||
</Component>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user) return deniedCompoinent;
|
|
||||||
if (
|
|
||||||
!user.permissions ||
|
|
||||||
user.permissions.indexOf(ADMIN) !== -1 ||
|
|
||||||
user.permissions.indexOf(permission) !== -1
|
|
||||||
) {
|
|
||||||
return grantedComponent;
|
|
||||||
}
|
|
||||||
return deniedCompoinent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PermissionComponent;
|
export default PermissionComponent;
|
||||||
|
@ -22,7 +22,7 @@ exports[`renders correctly with one feature 1`] = `
|
|||||||
>
|
>
|
||||||
<react-mdl-Switch
|
<react-mdl-Switch
|
||||||
checked={false}
|
checked={false}
|
||||||
disabled={true}
|
disabled={false}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
title="Toggle Another"
|
title="Toggle Another"
|
||||||
/>
|
/>
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { createStore } from 'redux';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { Map as $Map } from 'immutable';
|
||||||
|
|
||||||
import Feature from './../feature-list-item-component';
|
import Feature from './../feature-list-item-component';
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
|
import { UPDATE_FEATURE } from '../../../permissions';
|
||||||
|
|
||||||
jest.mock('react-mdl');
|
jest.mock('react-mdl');
|
||||||
|
|
||||||
@ -21,9 +25,11 @@ test('renders correctly with one feature', () => {
|
|||||||
],
|
],
|
||||||
createdAt: '2018-02-04T20:27:52.127Z',
|
createdAt: '2018-02-04T20:27:52.127Z',
|
||||||
};
|
};
|
||||||
|
const store = { user: new $Map({ profile: { permissions: [UPDATE_FEATURE] } }) };
|
||||||
const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} };
|
const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} };
|
||||||
const settings = { sort: 'name' };
|
const settings = { sort: 'name' };
|
||||||
const tree = renderer.create(
|
const tree = renderer.create(
|
||||||
|
<Provider store={createStore(state => state, store)}>
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<Feature
|
<Feature
|
||||||
key={0}
|
key={0}
|
||||||
@ -34,6 +40,7 @@ test('renders correctly with one feature', () => {
|
|||||||
toggleFeature={jest.fn()}
|
toggleFeature={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
|
</Provider>
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(tree).toMatchSnapshot();
|
expect(tree).toMatchSnapshot();
|
||||||
|
@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl';
|
import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl';
|
||||||
import Progress from './progress';
|
import Progress from './progress';
|
||||||
|
import PermissionComponent from '../common/permission-container';
|
||||||
|
import { UPDATE_FEATURE } from '../../permissions';
|
||||||
import { calc, styles as commonStyles } from '../common';
|
import { calc, styles as commonStyles } from '../common';
|
||||||
|
|
||||||
import styles from './feature.scss';
|
import styles from './feature.scss';
|
||||||
@ -14,7 +16,6 @@ const Feature = ({
|
|||||||
metricsLastHour = { yes: 0, no: 0, isFallback: true },
|
metricsLastHour = { yes: 0, no: 0, isFallback: true },
|
||||||
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
|
metricsLastMinute = { yes: 0, no: 0, isFallback: true },
|
||||||
revive,
|
revive,
|
||||||
updateable,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { name, description, enabled, strategies } = feature;
|
const { name, description, enabled, strategies } = feature;
|
||||||
const { showLastHour = false } = settings;
|
const { showLastHour = false } = settings;
|
||||||
@ -42,13 +43,27 @@ const Feature = ({
|
|||||||
<Progress strokeWidth={15} percentage={percent} isFallback={isStale} />
|
<Progress strokeWidth={15} percentage={percent} isFallback={isStale} />
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.listItemToggle}>
|
<span className={styles.listItemToggle}>
|
||||||
|
<PermissionComponent
|
||||||
|
permission={UPDATE_FEATURE}
|
||||||
|
component={
|
||||||
<Switch
|
<Switch
|
||||||
disabled={!updateable || toggleFeature === undefined}
|
disabled={toggleFeature === undefined}
|
||||||
title={`Toggle ${name}`}
|
title={`Toggle ${name}`}
|
||||||
key="left-actions"
|
key="left-actions"
|
||||||
onChange={() => toggleFeature(name)}
|
onChange={() => toggleFeature(name)}
|
||||||
checked={enabled}
|
checked={enabled}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
otherwise={
|
||||||
|
<Switch
|
||||||
|
disabled
|
||||||
|
title={`Toggle ${name}`}
|
||||||
|
key="left-actions"
|
||||||
|
onChange={() => toggleFeature(name)}
|
||||||
|
checked={enabled}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
|
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
|
||||||
<Link to={featureUrl} className={[commonStyles.listLink, commonStyles.truncate].join(' ')}>
|
<Link to={featureUrl} className={[commonStyles.listLink, commonStyles.truncate].join(' ')}>
|
||||||
@ -60,10 +75,16 @@ const Feature = ({
|
|||||||
{strategyChips}
|
{strategyChips}
|
||||||
{summaryChip}
|
{summaryChip}
|
||||||
</span>
|
</span>
|
||||||
{updateable && revive ? (
|
{revive ? (
|
||||||
|
<PermissionComponent
|
||||||
|
permission={UPDATE_FEATURE}
|
||||||
|
component={
|
||||||
<ListItemAction onClick={() => revive(feature.name)}>
|
<ListItemAction onClick={() => revive(feature.name)}>
|
||||||
<Icon name="undo" />
|
<Icon name="undo" />
|
||||||
</ListItemAction>
|
</ListItemAction>
|
||||||
|
}
|
||||||
|
otherwise={<span />}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span />
|
<span />
|
||||||
)}
|
)}
|
||||||
|
@ -8,10 +8,10 @@ import { HeaderTitle } from '../../common';
|
|||||||
class StrategiesSectionComponent extends React.Component {
|
class StrategiesSectionComponent extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
strategies: PropTypes.array.isRequired,
|
strategies: PropTypes.array.isRequired,
|
||||||
addStrategy: PropTypes.func.isRequired,
|
addStrategy: PropTypes.func,
|
||||||
removeStrategy: PropTypes.func.isRequired,
|
removeStrategy: PropTypes.func,
|
||||||
updateStrategy: PropTypes.func.isRequired,
|
updateStrategy: PropTypes.func,
|
||||||
fetchStrategies: PropTypes.func.isRequired,
|
fetchStrategies: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
@ -155,20 +155,29 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
{this.isFeatureView ? (
|
{this.isFeatureView ? (
|
||||||
<PermissionComponent
|
<PermissionComponent
|
||||||
permission={UPDATE_FEATURE}
|
permission={UPDATE_FEATURE}
|
||||||
component={Textfield}
|
component={
|
||||||
granted={{
|
<Textfield
|
||||||
onChange: v => setValue('description', v),
|
|
||||||
onBlur: updateFeatureToggle,
|
|
||||||
}}
|
|
||||||
denied={{
|
|
||||||
disabled: true,
|
|
||||||
}}
|
|
||||||
floatingLabel
|
floatingLabel
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
rows={1}
|
rows={1}
|
||||||
label="Description"
|
label="Description"
|
||||||
required
|
required
|
||||||
value={featureToggle.description}
|
value={featureToggle.description}
|
||||||
|
onChange={v => setValue('description', v)}
|
||||||
|
onBlur={updateFeatureToggle}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
otherwise={
|
||||||
|
<Textfield
|
||||||
|
disabled
|
||||||
|
floatingLabel
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
rows={1}
|
||||||
|
label="Description"
|
||||||
|
required
|
||||||
|
value={featureToggle.description}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Textfield
|
<Textfield
|
||||||
@ -194,19 +203,27 @@ export default class ViewFeatureToggleComponent extends React.Component {
|
|||||||
<span style={{ paddingRight: '24px' }}>
|
<span style={{ paddingRight: '24px' }}>
|
||||||
<PermissionComponent
|
<PermissionComponent
|
||||||
permission={UPDATE_FEATURE}
|
permission={UPDATE_FEATURE}
|
||||||
component={Switch}
|
component={
|
||||||
granted={{
|
<Switch
|
||||||
disabled: !this.isFeatureView,
|
disabled={!this.isFeatureView}
|
||||||
}}
|
|
||||||
denied={{
|
|
||||||
disabled: true,
|
|
||||||
}}
|
|
||||||
ripple
|
ripple
|
||||||
checked={featureToggle.enabled}
|
checked={featureToggle.enabled}
|
||||||
onChange={() => toggleFeature(featureToggle.name)}
|
onChange={() => toggleFeature(featureToggle.name)}
|
||||||
>
|
>
|
||||||
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
||||||
</PermissionComponent>
|
</Switch>
|
||||||
|
}
|
||||||
|
otherwise={
|
||||||
|
<Switch
|
||||||
|
disabled
|
||||||
|
ripple
|
||||||
|
checked={featureToggle.enabled}
|
||||||
|
onChange={() => toggleFeature(featureToggle.name)}
|
||||||
|
>
|
||||||
|
{featureToggle.enabled ? 'Enabled' : 'Disabled'}
|
||||||
|
</Switch>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{this.isFeatureView ? (
|
{this.isFeatureView ? (
|
||||||
|
Loading…
Reference in New Issue
Block a user