refactor: PageButtons component modernization
This commit is contained in:
@@ -1,96 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import selectors from 'data/selectors';
|
||||
import thunkActions from 'data/thunkActions';
|
||||
|
||||
import { PageButtons, mapStateToProps, mapDispatchToProps } from '.';
|
||||
|
||||
jest.mock('@edx/paragon', () => ({
|
||||
Button: () => 'Button',
|
||||
}));
|
||||
jest.mock('data/selectors', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
grades: {
|
||||
nextPage: jest.fn(state => ({ nextPage: state })),
|
||||
prevPage: jest.fn(state => ({ prevPage: state })),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('data/thunkActions', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
grades: {
|
||||
fetchPrevNextGrades: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
let props;
|
||||
let el;
|
||||
describe('PageButtons component', () => {
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
getPrevNextGrades: jest.fn(),
|
||||
nextPage: 'NEXT PAGE',
|
||||
prevPage: 'prev PAGE',
|
||||
};
|
||||
});
|
||||
describe('snapshots', () => {
|
||||
beforeEach(() => {
|
||||
el = shallow(<PageButtons {...props} />);
|
||||
el.instance.fetchNextGrades = jest.fn().mockName('fetchNextGrades');
|
||||
el.instance.fetchPrevGrades = jest.fn().mockName('fetchPrevGrades');
|
||||
});
|
||||
test('buttons enabled with both endpoints provided', () => {
|
||||
expect(el.instance().render()).toMatchSnapshot();
|
||||
});
|
||||
test('nextPage disabled if not provided', () => {
|
||||
el.setProps({ nextPage: undefined });
|
||||
expect(el.instance().render()).toMatchSnapshot();
|
||||
});
|
||||
test('prevPage disabled if not provided', () => {
|
||||
el.setProps({ prevPage: undefined });
|
||||
expect(el.instance().render()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('behavior', () => {
|
||||
beforeEach(() => {
|
||||
el = shallow(<PageButtons {...props} />);
|
||||
});
|
||||
describe('getPrevGrades', () => {
|
||||
it('calls props.getPrevNextGrades with props.prevPage', () => {
|
||||
el.instance().getPrevGrades();
|
||||
expect(props.getPrevNextGrades).toHaveBeenCalledWith(props.prevPage);
|
||||
});
|
||||
});
|
||||
describe('getNextGrades', () => {
|
||||
it('calls props.getPrevNextGrades with props.nextPage', () => {
|
||||
el.instance().getNextGrades();
|
||||
expect(props.getPrevNextGrades).toHaveBeenCalledWith(props.nextPage);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('mapStateToProps', () => {
|
||||
const testState = { l: 'eeeerroooooy', j: 'jjjjeeeeeeenkins' };
|
||||
let mapped;
|
||||
beforeEach(() => {
|
||||
mapped = mapStateToProps(testState);
|
||||
});
|
||||
test('nextPage from grades.nextPage', () => {
|
||||
expect(mapped.nextPage).toEqual(selectors.grades.nextPage(testState));
|
||||
});
|
||||
test('prevPage from grades.prevPage', () => {
|
||||
expect(mapped.prevPage).toEqual(selectors.grades.prevPage(testState));
|
||||
});
|
||||
});
|
||||
describe('mapDispatchToProps', () => {
|
||||
test('getPrevNextGrades from thunkActions.grades.fetchPrevNextGrades', () => {
|
||||
expect(
|
||||
mapDispatchToProps.getPrevNextGrades,
|
||||
).toEqual(thunkActions.grades.fetchPrevNextGrades);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,133 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PageButtons component snapshots buttons enabled with both endpoints provided 1`] = `
|
||||
<div
|
||||
className="d-flex justify-content-center"
|
||||
style={
|
||||
Object {
|
||||
"paddingBottom": "20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Previous Page"
|
||||
description="Grades tab Previous Page button text"
|
||||
id="gradebook.GradesView.PageButtons.prevPage"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Next Page"
|
||||
description="Grades tab Next Page button text"
|
||||
id="gradebook.GradesView.PageButtons.nextPage"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PageButtons component snapshots nextPage disabled if not provided 1`] = `
|
||||
<div
|
||||
className="d-flex justify-content-center"
|
||||
style={
|
||||
Object {
|
||||
"paddingBottom": "20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Previous Page"
|
||||
description="Grades tab Previous Page button text"
|
||||
id="gradebook.GradesView.PageButtons.prevPage"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Next Page"
|
||||
description="Grades tab Next Page button text"
|
||||
id="gradebook.GradesView.PageButtons.nextPage"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`PageButtons component snapshots prevPage disabled if not provided 1`] = `
|
||||
<div
|
||||
className="d-flex justify-content-center"
|
||||
style={
|
||||
Object {
|
||||
"paddingBottom": "20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled={true}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Previous Page"
|
||||
description="Grades tab Previous Page button text"
|
||||
id="gradebook.GradesView.PageButtons.prevPage"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="Next Page"
|
||||
description="Grades tab Next Page button text"
|
||||
id="gradebook.GradesView.PageButtons.nextPage"
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`PageButtons component render snapshot 1`] = `
|
||||
<div
|
||||
className="d-flex justify-content-center"
|
||||
style={
|
||||
Object {
|
||||
"paddingBottom": "20px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabled="prev-disabled"
|
||||
onClick={[MockFunction hooks.prev.onClick]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
prev-text
|
||||
</Button>
|
||||
<Button
|
||||
disabled="next-disabled"
|
||||
onClick={[MockFunction hooks.next.onClick]}
|
||||
style={
|
||||
Object {
|
||||
"margin": "20px",
|
||||
}
|
||||
}
|
||||
variant="outline-primary"
|
||||
>
|
||||
next-text
|
||||
</Button>
|
||||
</div>
|
||||
`;
|
||||
34
src/components/GradesView/PageButtons/hooks.js
Normal file
34
src/components/GradesView/PageButtons/hooks.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { selectors, thunkActions } from 'data/redux/hooks';
|
||||
import messages from './messages';
|
||||
|
||||
export const usePageButtonsData = () => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const { nextPage, prevPage } = selectors.grades.useGradeData();
|
||||
const getPrevNextGrades = thunkActions.grades.useFetchPrevNextGrades();
|
||||
|
||||
const getPrevGrades = () => {
|
||||
getPrevNextGrades(prevPage);
|
||||
};
|
||||
|
||||
const getNextGrades = () => {
|
||||
getPrevNextGrades(nextPage);
|
||||
};
|
||||
|
||||
return {
|
||||
prev: {
|
||||
disabled: !prevPage,
|
||||
onClick: getPrevGrades,
|
||||
text: formatMessage(messages.prevPage),
|
||||
},
|
||||
next: {
|
||||
disabled: !nextPage,
|
||||
onClick: getNextGrades,
|
||||
text: formatMessage(messages.nextPage),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default usePageButtonsData;
|
||||
77
src/components/GradesView/PageButtons/hooks.test.js
Normal file
77
src/components/GradesView/PageButtons/hooks.test.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { formatMessage } from 'testUtils';
|
||||
import { selectors, thunkActions } from 'data/redux/hooks';
|
||||
|
||||
import usePageButtonsData from './hooks';
|
||||
import messages from './messages';
|
||||
|
||||
jest.mock('data/redux/hooks', () => ({
|
||||
selectors: {
|
||||
grades: { useGradeData: jest.fn() },
|
||||
},
|
||||
thunkActions: {
|
||||
grades: { useFetchPrevNextGrades: jest.fn() },
|
||||
},
|
||||
}));
|
||||
|
||||
const gradeData = { nextPage: 'test-next-page', prevPage: 'test-prev-page' };
|
||||
selectors.grades.useGradeData.mockReturnValue(gradeData);
|
||||
|
||||
const fetchGrades = jest.fn();
|
||||
thunkActions.grades.useFetchPrevNextGrades.mockReturnValue(fetchGrades);
|
||||
|
||||
let out;
|
||||
describe('usePageButtonsData', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
out = usePageButtonsData();
|
||||
});
|
||||
describe('behavior', () => {
|
||||
it('initializes intl hook', () => {
|
||||
expect(useIntl).toHaveBeenCalled();
|
||||
});
|
||||
it('initializes redux hooks', () => {
|
||||
expect(selectors.grades.useGradeData).toHaveBeenCalled();
|
||||
expect(thunkActions.grades.useFetchPrevNextGrades).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('output', () => {
|
||||
describe('prev button entry', () => {
|
||||
it('is disabled iff prevPage is not provided', () => {
|
||||
expect(out.prev.disabled).toEqual(false);
|
||||
selectors.grades.useGradeData.mockReturnValueOnce({
|
||||
...gradeData,
|
||||
prevPage: undefined,
|
||||
});
|
||||
out = usePageButtonsData();
|
||||
expect(out.prev.disabled).toEqual(true);
|
||||
});
|
||||
it('calls fetch with prevPage on click', () => {
|
||||
out.prev.onClick();
|
||||
expect(fetchGrades).toHaveBeenCalledWith(gradeData.prevPage);
|
||||
});
|
||||
test('text display', () => {
|
||||
expect(out.prev.text).toEqual(formatMessage(messages.prevPage));
|
||||
});
|
||||
});
|
||||
describe('next button entry', () => {
|
||||
it('is disabled iff nextPage is not provided', () => {
|
||||
expect(out.next.disabled).toEqual(false);
|
||||
selectors.grades.useGradeData.mockReturnValueOnce({
|
||||
...gradeData,
|
||||
nextPage: undefined,
|
||||
});
|
||||
out = usePageButtonsData();
|
||||
expect(out.next.disabled).toEqual(true);
|
||||
});
|
||||
it('calls fetch with prevPage on click', () => {
|
||||
out.next.onClick();
|
||||
expect(fetchGrades).toHaveBeenCalledWith(gradeData.nextPage);
|
||||
});
|
||||
test('text display', () => {
|
||||
expect(out.next.text).toEqual(formatMessage(messages.nextPage));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,75 +1,37 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Button } from '@edx/paragon';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import selectors from 'data/selectors';
|
||||
import thunkActions from 'data/thunkActions';
|
||||
import messages from './messages';
|
||||
import usePageButtonsData from './hooks';
|
||||
|
||||
export class PageButtons extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.getPrevGrades = this.getPrevGrades.bind(this);
|
||||
this.getNextGrades = this.getNextGrades.bind(this);
|
||||
}
|
||||
export const PageButtons = () => {
|
||||
const { prev, next } = usePageButtonsData();
|
||||
|
||||
getPrevGrades() {
|
||||
this.props.getPrevNextGrades(this.props.prevPage);
|
||||
}
|
||||
|
||||
getNextGrades() {
|
||||
this.props.getPrevNextGrades(this.props.nextPage);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
className="d-flex justify-content-center"
|
||||
style={{ paddingBottom: '20px' }}
|
||||
return (
|
||||
<div
|
||||
className="d-flex justify-content-center"
|
||||
style={{ paddingBottom: '20px' }}
|
||||
>
|
||||
<Button
|
||||
style={{ margin: '20px' }}
|
||||
variant="outline-primary"
|
||||
disabled={prev.disabled}
|
||||
onClick={prev.onClick}
|
||||
>
|
||||
<Button
|
||||
style={{ margin: '20px' }}
|
||||
variant="outline-primary"
|
||||
disabled={!this.props.prevPage}
|
||||
onClick={this.getPrevGrades}
|
||||
>
|
||||
<FormattedMessage {...messages.prevPage} />
|
||||
</Button>
|
||||
<Button
|
||||
style={{ margin: '20px' }}
|
||||
variant="outline-primary"
|
||||
disabled={!this.props.nextPage}
|
||||
onClick={this.getNextGrades}
|
||||
>
|
||||
<FormattedMessage {...messages.nextPage} />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PageButtons.defaultProps = {
|
||||
nextPage: '',
|
||||
prevPage: '',
|
||||
{prev.text}
|
||||
</Button>
|
||||
<Button
|
||||
style={{ margin: '20px' }}
|
||||
variant="outline-primary"
|
||||
disabled={next.disabled}
|
||||
onClick={next.onClick}
|
||||
>
|
||||
{next.text}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PageButtons.propTypes = {
|
||||
// redux
|
||||
getPrevNextGrades: PropTypes.func.isRequired,
|
||||
nextPage: PropTypes.string,
|
||||
prevPage: PropTypes.string,
|
||||
};
|
||||
PageButtons.propTypes = {};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
nextPage: selectors.grades.nextPage(state),
|
||||
prevPage: selectors.grades.prevPage(state),
|
||||
});
|
||||
|
||||
export const mapDispatchToProps = {
|
||||
getPrevNextGrades: thunkActions.grades.fetchPrevNextGrades,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PageButtons);
|
||||
export default PageButtons;
|
||||
|
||||
53
src/components/GradesView/PageButtons/index.test.jsx
Normal file
53
src/components/GradesView/PageButtons/index.test.jsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { Button } from '@edx/paragon';
|
||||
|
||||
import usePageButtonsData from './hooks';
|
||||
import PageButtons from '.';
|
||||
|
||||
jest.mock('./hooks', () => jest.fn());
|
||||
|
||||
const hookProps = {
|
||||
prev: {
|
||||
disabled: 'prev-disabled',
|
||||
onClick: jest.fn().mockName('hooks.prev.onClick'),
|
||||
text: 'prev-text',
|
||||
},
|
||||
next: {
|
||||
disabled: 'next-disabled',
|
||||
onClick: jest.fn().mockName('hooks.next.onClick'),
|
||||
text: 'next-text',
|
||||
},
|
||||
};
|
||||
usePageButtonsData.mockReturnValue(hookProps);
|
||||
|
||||
let el;
|
||||
describe('PageButtons component', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
el = shallow(<PageButtons />);
|
||||
});
|
||||
describe('behavior', () => {
|
||||
it('initializes component hooks', () => {
|
||||
expect(usePageButtonsData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
describe('render', () => {
|
||||
test('snapshot', () => {
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
test('prev button', () => {
|
||||
const button = el.find(Button).at(0);
|
||||
expect(button.props().disabled).toEqual(hookProps.prev.disabled);
|
||||
expect(button.props().onClick).toEqual(hookProps.prev.onClick);
|
||||
expect(button.text()).toEqual(hookProps.prev.text);
|
||||
});
|
||||
test('next button', () => {
|
||||
const button = el.find(Button).at(1);
|
||||
expect(button.props().disabled).toEqual(hookProps.next.disabled);
|
||||
expect(button.props().onClick).toEqual(hookProps.next.onClick);
|
||||
expect(button.text()).toEqual(hookProps.next.text);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user