feat: Ora mobile responsive (#336)
Co-authored-by: ihor-romaniuk <ihor.romaniuk@raccoongang.com>
This commit is contained in:
18
.eslintrc.js
18
.eslintrc.js
@@ -7,20 +7,28 @@ const config = createConfig('eslint', {
|
|||||||
'import/no-named-as-default-member': 'off',
|
'import/no-named-as-default-member': 'off',
|
||||||
'import/no-import-module-exports': 'off',
|
'import/no-import-module-exports': 'off',
|
||||||
'import/no-self-import': 'off',
|
'import/no-self-import': 'off',
|
||||||
'spaced-comment': ['error', 'always', { 'block': { 'exceptions': ['*'] } }],
|
'spaced-comment': ['error', 'always', { block: { exceptions: ['*'] } }],
|
||||||
'react-hooks/rules-of-hooks': 'off',
|
'react-hooks/rules-of-hooks': 'off',
|
||||||
"react/forbid-prop-types": ["error", { "forbid": ["any", "array"] }], // arguable object proptype is use when I do not care about the shape of the object
|
'react/forbid-prop-types': ['error', { forbid: ['any', 'array'] }], // arguable object proptype is use when I do not care about the shape of the object
|
||||||
'no-import-assign': 'off',
|
'no-import-assign': 'off',
|
||||||
'no-promise-executor-return': 'off',
|
'no-promise-executor-return': 'off',
|
||||||
'import/no-cycle': 'off',
|
'import/no-cycle': 'off',
|
||||||
},
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['**/*.test.{js,jsx,ts,tsx}'],
|
||||||
|
rules: {
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
config.settings = {
|
config.settings = {
|
||||||
"import/resolver": {
|
'import/resolver': {
|
||||||
node: {
|
node: {
|
||||||
paths: ["src", "node_modules"],
|
paths: ['src', 'node_modules'],
|
||||||
extensions: [".js", ".jsx"],
|
extensions: ['.js', '.jsx'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { screen } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { selectors } from 'data/redux';
|
import { selectors } from 'data/redux';
|
||||||
|
|
||||||
import { renderWithIntl } from './testUtils';
|
import { renderWithIntl } from './testUtils';
|
||||||
import { App, mapStateToProps } from './App';
|
import { App, mapStateToProps } from './App';
|
||||||
|
|
||||||
@@ -22,6 +23,14 @@ jest.mock('containers/DemoWarning', () => function DemoWarning() {
|
|||||||
return <div role="alert" data-testid="demo-warning">Demo Warning</div>;
|
return <div role="alert" data-testid="demo-warning">Demo Warning</div>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('@edx/frontend-component-header', () => ({
|
||||||
|
LearningHeader: ({ courseTitle, courseNumber, courseOrg }) => (
|
||||||
|
<div data-testid="header">
|
||||||
|
Header - {courseTitle} {courseNumber} {courseOrg}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('data/redux', () => ({
|
jest.mock('data/redux', () => ({
|
||||||
selectors: {
|
selectors: {
|
||||||
app: {
|
app: {
|
||||||
@@ -53,7 +62,7 @@ describe('App component', () => {
|
|||||||
renderWithIntl(<App {...defaultProps} />);
|
renderWithIntl(<App {...defaultProps} />);
|
||||||
const org = screen.getByText((text) => text.includes('test-org'));
|
const org = screen.getByText((text) => text.includes('test-org'));
|
||||||
expect(org).toBeInTheDocument();
|
expect(org).toBeInTheDocument();
|
||||||
const title = screen.getByText('Test Course');
|
const title = screen.getByText((content) => content.includes('Test Course'));
|
||||||
expect(title).toBeInTheDocument();
|
expect(title).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||||
import { ConfirmModal } from './ConfirmModal';
|
import { ConfirmModal } from './ConfirmModal';
|
||||||
|
|
||||||
describe('ConfirmModal', () => {
|
describe('ConfirmModal', () => {
|
||||||
@@ -18,24 +19,40 @@ describe('ConfirmModal', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not render content when modal is closed', () => {
|
it('should not render content when modal is closed', () => {
|
||||||
render(<ConfirmModal {...props} />);
|
render(
|
||||||
|
<IntlProvider locale="en">
|
||||||
|
<ConfirmModal {...props} />
|
||||||
|
</IntlProvider>,
|
||||||
|
);
|
||||||
expect(screen.queryByText(props.content)).toBeNull();
|
expect(screen.queryByText(props.content)).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display content when modal is open', () => {
|
it('should display content when modal is open', () => {
|
||||||
render(<ConfirmModal {...props} isOpen />);
|
render(
|
||||||
|
<IntlProvider locale="en">
|
||||||
|
<ConfirmModal {...props} isOpen />
|
||||||
|
</IntlProvider>,
|
||||||
|
);
|
||||||
expect(screen.getByText(props.content)).toBeInTheDocument();
|
expect(screen.getByText(props.content)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onCancel when cancel button is clicked', async () => {
|
it('should call onCancel when cancel button is clicked', async () => {
|
||||||
render(<ConfirmModal {...props} isOpen />);
|
render(
|
||||||
|
<IntlProvider locale="en">
|
||||||
|
<ConfirmModal {...props} isOpen />
|
||||||
|
</IntlProvider>,
|
||||||
|
);
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
await user.click(screen.getByText(props.cancelText));
|
await user.click(screen.getByText(props.cancelText));
|
||||||
expect(props.onCancel).toHaveBeenCalledTimes(1);
|
expect(props.onCancel).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call onConfirm when confirm button is clicked', async () => {
|
it('should call onConfirm when confirm button is clicked', async () => {
|
||||||
render(<ConfirmModal {...props} isOpen />);
|
render(
|
||||||
|
<IntlProvider locale="en">
|
||||||
|
<ConfirmModal {...props} isOpen />
|
||||||
|
</IntlProvider>,
|
||||||
|
);
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
await user.click(screen.getByText(props.confirmText));
|
await user.click(screen.getByText(props.confirmText));
|
||||||
expect(props.onConfirm).toHaveBeenCalledTimes(1);
|
expect(props.onConfirm).toHaveBeenCalledTimes(1);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const InfoPopover = (
|
|||||||
return (
|
return (
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
trigger="focus"
|
trigger="focus"
|
||||||
placement="right-end"
|
placement="left-end"
|
||||||
flip
|
flip
|
||||||
overlay={(
|
overlay={(
|
||||||
<Popover id="info-popover" className="overlay-help-popover">
|
<Popover id="info-popover" className="overlay-help-popover">
|
||||||
|
|||||||
@@ -23,4 +23,14 @@ span.pgn__icon.breadcrumb-arrow {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (--pgn-size-breakpoint-max-width-xs) {
|
||||||
|
.badge {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pgn__table-actions > div:first-of-type {
|
||||||
|
z-index: var(--pgn-elevation-modal-zindex) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,3 +33,10 @@
|
|||||||
padding-bottom:var(--pgn-spacing-spacer-3);
|
padding-bottom:var(--pgn-spacing-spacer-3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (--pgn-size-breakpoint-max-width-xs) {
|
||||||
|
.overlay-help-popover {
|
||||||
|
max-width: calc(100% - 60px) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const ReviewActions = ({
|
|||||||
{ gradingStatus && (
|
{ gradingStatus && (
|
||||||
<StatusBadge className="review-actions-status mr-3" status={gradingStatus} />
|
<StatusBadge className="review-actions-status mr-3" status={gradingStatus} />
|
||||||
)}
|
)}
|
||||||
<span className="small">
|
<span className="small text-nowrap">
|
||||||
{pointsPossible && (
|
{pointsPossible && (
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
{...messages.pointsDisplay}
|
{...messages.pointsDisplay}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
import { FullscreenModal } from '@openedx/paragon';
|
import { FullscreenModal, Truncate, useMediaQuery } from '@openedx/paragon';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
|
||||||
import LoadingMessage from 'components/LoadingMessage';
|
import LoadingMessage from 'components/LoadingMessage';
|
||||||
@@ -28,9 +28,12 @@ export const ReviewModal = () => {
|
|||||||
isOpen,
|
isOpen,
|
||||||
closeConfirmModalProps,
|
closeConfirmModalProps,
|
||||||
} = hooks.rendererHooks({ dispatch, intl });
|
} = hooks.rendererHooks({ dispatch, intl });
|
||||||
|
|
||||||
|
const isMobile = useMediaQuery({ query: '(max-width: 768px)' });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FullscreenModal
|
<FullscreenModal
|
||||||
title={title}
|
title={isMobile ? <Truncate lines={3}>{title}</Truncate> : title}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
beforeBodyNode={(
|
beforeBodyNode={(
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user