GH-6452 Migrate installed_command.jsx to be pure and use Redux (#6903)

* Migrate installed_command.jsx to be pure and use Redux

* Add test for InstalledCommand component

* Fix failing test and typo

* Whoops. Revert back deleted filter

* Add filter test

* Remove commands related code from /stores/integration_store.jsx
Этот коммит содержится в:
94117nl
2017-08-03 08:25:07 -05:00
коммит произвёл Harrison Healey
родитель b54d134299
Коммит ea095d6206
6 изменённых файлов: 230 добавлений и 57 удалений

Просмотреть файл

@@ -33,7 +33,7 @@ export default class CommandsContainer extends React.PureComponent {
users: PropTypes.object,
/**
* Installed splash commands to display
* Installed slash commands to display
*/
commands: PropTypes.array,

Просмотреть файл

@@ -1,43 +1,59 @@
import PropTypes from 'prop-types';
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router';
import {FormattedMessage} from 'react-intl';
import DeleteIntegration from './delete_integration.jsx';
export default class InstalledCommand extends React.Component {
static get propTypes() {
return {
team: PropTypes.object.isRequired,
command: PropTypes.object.isRequired,
onRegenToken: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
filter: PropTypes.string,
creator: PropTypes.object.isRequired,
canChange: PropTypes.bool.isRequired
};
export default class InstalledCommand extends React.PureComponent {
static propTypes = {
/**
* The team data
*/
team: PropTypes.object.isRequired,
/**
* Installed slash command to display
*/
command: PropTypes.object.isRequired,
/**
* The function to call when Regenerate Token link is clicked
*/
onRegenToken: PropTypes.func.isRequired,
/**
* The function to call when Delete link is clicked
*/
onDelete: PropTypes.func.isRequired,
/**
* Set to filter command, comes from BackstageList
*/
filter: PropTypes.string,
/**
* The creator user data
*/
creator: PropTypes.object.isRequired,
/**
* Set to show edit link
*/
canChange: PropTypes.bool.isRequired
}
constructor(props) {
super(props);
this.handleRegenToken = this.handleRegenToken.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.matchesFilter = this.matchesFilter.bind(this);
}
handleRegenToken(e) {
handleRegenToken = (e) => {
e.preventDefault();
this.props.onRegenToken(this.props.command);
}
handleDelete() {
handleDelete = () => {
this.props.onDelete(this.props.command);
}
@@ -60,6 +76,7 @@ export default class InstalledCommand extends React.Component {
}
let name;
if (command.display_name) {
name = command.display_name;
} else {

Просмотреть файл

@@ -30,7 +30,7 @@ export default class InstalledCommands extends React.PureComponent {
users: PropTypes.object,
/**
* Installed splash commands to display
* Installed slash commands to display
*/
commands: PropTypes.array,
@@ -40,7 +40,7 @@ export default class InstalledCommands extends React.PureComponent {
loading: PropTypes.bool,
/**
* Set to allow changes to installed splash commands
* Set to allow changes to installed slash commands
*/
isAdmin: PropTypes.bool,

Просмотреть файл

@@ -91,36 +91,6 @@ class IntegrationStore extends EventEmitter {
return store.getState().entities.integrations.outgoingHooks[id];
}
hasReceivedCommands(teamId) {
const commands = store.getState().entities.integrations.commands;
let hasTeam = false;
Object.values(commands).forEach((command) => {
if (command.team_id === teamId) {
hasTeam = true;
}
});
return hasTeam;
}
getCommands(teamId) {
const commands = store.getState().entities.integrations.commands;
const teamCommands = [];
Object.values(commands).forEach((command) => {
if (command.team_id === teamId) {
teamCommands.push(command);
}
});
return teamCommands;
}
getCommand(teamId, id) {
return store.getState().entities.integrations.commands[id];
}
hasReceivedOAuthApps() {
return Object.keys(store.getState().entities.integrations.oauthApps).length > 0;
}

Просмотреть файл

@@ -0,0 +1,103 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/integrations/InstalledCommand should filter out command 1`] = `null`;
exports[`components/integrations/InstalledCommand should match snapshot 1`] = `
<div
className="backstage-list__item"
>
<div
className="item-details"
>
<div
className="item-details__row"
>
<span
className="item-details__name"
>
test
</span>
<span
className="item-details__trigger"
>
- /trigger test
</span>
</div>
<div
className="item-details__row"
>
<span
className="item-details__description"
>
test
</span>
</div>
<div
className="item-details__row"
>
<span
className="item-details__token"
>
<FormattedMessage
defaultMessage="Token: {token}"
id="installed_integrations.token"
values={
Object {
"token": "testToken",
}
}
/>
</span>
</div>
<div
className="item-details__row"
>
<span
className="item-details__creation"
>
<FormattedMessage
defaultMessage="Created by {creator} on {createAt, date, full}"
id="installed_integrations.creation"
values={
Object {
"createAt": "1499722850203",
"creator": "test",
}
}
/>
</span>
</div>
</div>
<div
className="item-actions"
>
<a
href="#"
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Regenerate Token"
id="installed_integrations.regenToken"
values={Object {}}
/>
</a>
-
<Link
onlyActiveOnIndex={false}
style={Object {}}
to="/test/integrations/commands/edit?id=r5tpgt4iepf45jt768jz84djic"
>
<FormattedMessage
defaultMessage="Edit"
id="installed_integrations.edit"
values={Object {}}
/>
</Link>
-
<DeleteIntegration
messageId="installed_commands.delete.confirm"
onDelete={[Function]}
/>
</div>
</div>
`;

Просмотреть файл

@@ -0,0 +1,83 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import InstalledCommand from 'components/integrations/components/installed_command.jsx';
describe('components/integrations/InstalledCommand', () => {
const emptyFunction = jest.fn();
const command = {
id: 'r5tpgt4iepf45jt768jz84djic',
display_name: 'test',
description: 'test',
trigger: 'trigger',
auto_complete: 'test',
auto_complete_hint: 'test',
token: 'testToken',
create_at: '1499722850203'
};
test('should match snapshot', () => {
const wrapper = shallow(
<InstalledCommand
team={{
name: 'test'
}}
command={command}
onRegenToken={emptyFunction}
onDelete={emptyFunction}
filter={'trigger'}
creator={{
username: 'test'
}}
canChange={true}
/>
);
expect(wrapper).toMatchSnapshot();
});
test('should call onRegenToken function', () => {
const onRegenToken = jest.fn();
const wrapper = shallow(
<InstalledCommand
team={{
name: 'test'
}}
command={command}
onRegenToken={onRegenToken}
onDelete={emptyFunction}
filter={''}
creator={{
username: 'test'
}}
canChange={true}
/>
);
wrapper.find('div.item-actions a').first().simulate('click', {preventDefault() {
return jest.fn();
}});
expect(onRegenToken).toBeCalled();
});
test('should filter out command', () => {
const wrapper = shallow(
<InstalledCommand
team={{
name: 'test'
}}
command={command}
onRegenToken={emptyFunction}
onDelete={emptyFunction}
filter={'filter'}
creator={{
username: 'test'
}}
canChange={true}
/>
);
expect(wrapper).toMatchSnapshot();
});
});