Compare commits
12 Commits
open-relea
...
js/getfeed
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2e46e387 | ||
|
|
7ce1808b0a | ||
|
|
e62cc3d135 | ||
|
|
c7a3cc0341 | ||
|
|
113f66783a | ||
|
|
19230c4506 | ||
|
|
ae1702d182 | ||
|
|
67789481fb | ||
|
|
543cd623e1 | ||
|
|
ba31b713e2 | ||
|
|
84fe2c6628 | ||
|
|
b87447b543 |
1
.env
1
.env
@@ -32,3 +32,4 @@ ENTERPRISE_MARKETING_UTM_CAMPAIGN=''
|
|||||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM=''
|
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM=''
|
||||||
APP_ID=''
|
APP_ID=''
|
||||||
MFE_CONFIG_API_URL=''
|
MFE_CONFIG_API_URL=''
|
||||||
|
DISPLAY_FEEDBACK_WIDGET='true'
|
||||||
|
|||||||
@@ -39,3 +39,4 @@ ENTERPRISE_MARKETING_UTM_CAMPAIGN='example.com Referral'
|
|||||||
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM='Footer'
|
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM='Footer'
|
||||||
APP_ID=''
|
APP_ID=''
|
||||||
MFE_CONFIG_API_URL=''
|
MFE_CONFIG_API_URL=''
|
||||||
|
DISPLAY_FEEDBACK_WIDGET='false'
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ dist/
|
|||||||
node_modules/
|
node_modules/
|
||||||
src/postcss.config.js
|
src/postcss.config.js
|
||||||
src/segment.js
|
src/segment.js
|
||||||
|
src/lightning.js
|
||||||
|
|||||||
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -11,18 +11,22 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node: [16]
|
||||||
|
npm: [8.5.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Setup Nodejs Env
|
|
||||||
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup Nodejs
|
- name: Setup Nodejs
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VER }}
|
node-version: ${{ matrix.node }}
|
||||||
|
|
||||||
|
- name: Install npm 8.5.x
|
||||||
|
run: npm install -g npm@${{ matrix.npm }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
2
.github/workflows/lockfileversion-check.yml
vendored
2
.github/workflows/lockfileversion-check.yml
vendored
@@ -10,4 +10,4 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
version-check:
|
version-check:
|
||||||
uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master
|
uses: openedx/.github/.github/workflows/lockfileversion-check.yml@master
|
||||||
|
|||||||
5
.github/workflows/npm-publish.yml
vendored
5
.github/workflows/npm-publish.yml
vendored
@@ -15,13 +15,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Nodejs Env
|
|
||||||
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VER }}
|
node-version: 12
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -4,6 +4,7 @@ npm-install-%: ## install specified % npm package
|
|||||||
export TRANSIFEX_RESOURCE = frontend-app-gradebook
|
export TRANSIFEX_RESOURCE = frontend-app-gradebook
|
||||||
transifex_langs = "ar,de,es_419,fa_IR,fr,fr_CA,hi,it,pt,ru,uk,zh_CN"
|
transifex_langs = "ar,de,es_419,fa_IR,fr,fr_CA,hi,it,pt,ru,uk,zh_CN"
|
||||||
|
|
||||||
|
intl_imports = ./node_modules/.bin/intl-imports.js
|
||||||
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
||||||
i18n = ./src/i18n
|
i18n = ./src/i18n
|
||||||
transifex_input = $(i18n)/transifex_input.json
|
transifex_input = $(i18n)/transifex_input.json
|
||||||
@@ -54,9 +55,23 @@ push_translations:
|
|||||||
# Pushing comments to Transifex...
|
# Pushing comments to Transifex...
|
||||||
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
|
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
|
||||||
|
|
||||||
|
ifeq ($(OPENEDX_ATLAS_PULL),)
|
||||||
# Pulls translations from Transifex.
|
# Pulls translations from Transifex.
|
||||||
pull_translations:
|
pull_translations:
|
||||||
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
|
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
|
||||||
|
else
|
||||||
|
# Experimental: OEP-58 Pulls translations using atlas
|
||||||
|
pull_translations:
|
||||||
|
rm -rf src/i18n/messages
|
||||||
|
mkdir src/i18n/messages
|
||||||
|
cd src/i18n/messages \
|
||||||
|
&& atlas pull --filter=$(transifex_langs) \
|
||||||
|
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
|
||||||
|
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
|
||||||
|
translations/frontend-app-gradebook/src/i18n/messages:frontend-app-gradebook
|
||||||
|
|
||||||
|
$(intl_imports) frontend-component-header frontend-component-footer frontend-app-gradebook
|
||||||
|
endif
|
||||||
|
|
||||||
# This target is used by CI.
|
# This target is used by CI.
|
||||||
validate-no-uncommitted-package-lock-changes:
|
validate-no-uncommitted-package-lock-changes:
|
||||||
|
|||||||
28904
package-lock.json
generated
28904
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@edx/frontend-app-gradebook",
|
"name": "@edx/frontend-app-gradebook",
|
||||||
"version": "1.6.1",
|
"version": "1.6.2",
|
||||||
"description": "edx editable gradebook-ui to manipulate grade overrides on subsections",
|
"description": "edx editable gradebook-ui to manipulate grade overrides on subsections",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
"extends @edx/browserslist-config"
|
"extends @edx/browserslist-config"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@edx/brand": "npm:@edx/brand-edx.org@^1.3.2",
|
"@edx/brand": "npm:@edx/brand-openedx@^1.2.0",
|
||||||
"@edx/frontend-component-footer": "^11.1.1",
|
"@edx/frontend-component-footer": "^12.0.0",
|
||||||
"@edx/frontend-component-header": "^3.1.1",
|
"@edx/frontend-component-header": "^4.0.0",
|
||||||
"@edx/frontend-platform": "2.5.0",
|
"@edx/frontend-platform": "^4.2.0",
|
||||||
"@edx/paragon": "^19.25.4",
|
"@edx/paragon": "^19.25.4",
|
||||||
"@edx/reactifex": "^2.1.1",
|
"@edx/reactifex": "^2.1.1",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
"@fortawesome/fontawesome-svg-core": "^1.2.25",
|
||||||
@@ -47,12 +47,11 @@
|
|||||||
"enzyme-to-json": "^3.6.2",
|
"enzyme-to-json": "^3.6.2",
|
||||||
"font-awesome": "4.7.0",
|
"font-awesome": "4.7.0",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.8.1",
|
||||||
"query-string": "6.13.0",
|
"query-string": "6.13.0",
|
||||||
"react": "16.14.0",
|
"react": "16.14.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet": "^6.1.0",
|
||||||
"react-intl": "^2.9.0",
|
|
||||||
"react-redux": "^7.1.1",
|
"react-redux": "^7.1.1",
|
||||||
"react-router": "5.2.0",
|
"react-router": "5.2.0",
|
||||||
"react-router-dom": "5.2.0",
|
"react-router-dom": "5.2.0",
|
||||||
@@ -68,7 +67,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@edx/browserslist-config": "^1.1.1",
|
"@edx/browserslist-config": "^1.1.1",
|
||||||
"@edx/frontend-build": "12.8.27",
|
"@edx/frontend-build": "^12.4.15",
|
||||||
"@testing-library/react": "^12.1.0",
|
"@testing-library/react": "^12.1.0",
|
||||||
"axios": "0.21.2",
|
"axios": "0.21.2",
|
||||||
"axios-mock-adapter": "^1.17.0",
|
"axios-mock-adapter": "^1.17.0",
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ exports[`GradebookHeader component snapshots default values (grades frozen, cann
|
|||||||
<div
|
<div
|
||||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||||
>
|
>
|
||||||
<h2
|
<h2>
|
||||||
className="text-break"
|
|
||||||
>
|
|
||||||
fakeID
|
fakeID
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,9 +75,7 @@ exports[`GradebookHeader component snapshots grades frozen, can view. grades fro
|
|||||||
<div
|
<div
|
||||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||||
>
|
>
|
||||||
<h2
|
<h2>
|
||||||
className="text-break"
|
|
||||||
>
|
|
||||||
fakeID
|
fakeID
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -125,9 +121,7 @@ exports[`GradebookHeader component snapshots grades frozen, cannot view unauthor
|
|||||||
<div
|
<div
|
||||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||||
>
|
>
|
||||||
<h2
|
<h2>
|
||||||
className="text-break"
|
|
||||||
>
|
|
||||||
fakeID
|
fakeID
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,9 +177,7 @@ exports[`GradebookHeader component snapshots show bulk management, active view i
|
|||||||
<div
|
<div
|
||||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||||
>
|
>
|
||||||
<h2
|
<h2>
|
||||||
className="text-break"
|
|
||||||
>
|
|
||||||
fakeID
|
fakeID
|
||||||
</h2>
|
</h2>
|
||||||
<Button
|
<Button
|
||||||
@@ -241,9 +233,7 @@ exports[`GradebookHeader component snapshots show bulk management, active view i
|
|||||||
<div
|
<div
|
||||||
className="subtitle-row d-flex justify-content-between align-items-center"
|
className="subtitle-row d-flex justify-content-between align-items-center"
|
||||||
>
|
>
|
||||||
<h2
|
<h2>
|
||||||
className="text-break"
|
|
||||||
>
|
|
||||||
fakeID
|
fakeID
|
||||||
</h2>
|
</h2>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export class GradebookHeader extends React.Component {
|
|||||||
<FormattedMessage {...messages.gradebook} />
|
<FormattedMessage {...messages.gradebook} />
|
||||||
</h1>
|
</h1>
|
||||||
<div className="subtitle-row d-flex justify-content-between align-items-center">
|
<div className="subtitle-row d-flex justify-content-between align-items-center">
|
||||||
<h2 className="text-break">{this.props.courseId}</h2>
|
<h2>{this.props.courseId}</h2>
|
||||||
{ this.props.showBulkManagement && (
|
{ this.props.showBulkManagement && (
|
||||||
<Button
|
<Button
|
||||||
variant="tertiary"
|
variant="tertiary"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Form } from '@edx/paragon';
|
|||||||
|
|
||||||
import selectors from 'data/selectors';
|
import selectors from 'data/selectors';
|
||||||
import actions from 'data/actions';
|
import actions from 'data/actions';
|
||||||
import { getLocalizedSlash } from 'i18n';
|
import { getLocalizedSlash } from 'i18n/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <AdjustedGradeInput />
|
* <AdjustedGradeInput />
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const FilterMenuToggle = ({ toggleFilterDrawer }) => (
|
|||||||
className="btn-primary align-self-start"
|
className="btn-primary align-self-start"
|
||||||
onClick={toggleFilterDrawer}
|
onClick={toggleFilterDrawer}
|
||||||
>
|
>
|
||||||
<Icon className="fa fa-filter mr-1" /> <FormattedMessage {...messages.editFilters} />
|
<Icon className="fa fa-filter" /> <FormattedMessage {...messages.editFilters} />
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -29,16 +29,16 @@ Username.propTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fields.Email
|
* Fields.Text
|
||||||
* Simple label field for email value.
|
* Simple label field for text value.
|
||||||
* @param {string} email - email for display
|
* @param {string} value - value for display
|
||||||
*/
|
*/
|
||||||
const Email = ({ email }) => <span className="wrap-text-in-cell">{email}</span>;
|
const Text = ({ value }) => (<span className="wrap-text-in-cell">{value}</span>);
|
||||||
Email.propTypes = {
|
Text.propTypes = {
|
||||||
email: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StrictDict({
|
export default StrictDict({
|
||||||
Email,
|
Text,
|
||||||
Username,
|
Username,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ describe('Gradebook Table Fields', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Email', () => {
|
describe('Text', () => {
|
||||||
const email = 'myTag@place.com';
|
const value = 'myTag@place.com';
|
||||||
test('snapshot', () => {
|
test('snapshot', () => {
|
||||||
expect(shallow(<Fields.Email email={email} />)).toMatchSnapshot();
|
expect(shallow(<Fields.Text value={value} />)).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
test('wraps entry email', () => {
|
test('wraps entry value', () => {
|
||||||
expect(shallow(<Fields.Email email={email} />).text()).toEqual(email);
|
expect(shallow(<Fields.Text value={value} />).text()).toEqual(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -45,6 +45,13 @@ const TotalGradeLabelReplacement = () => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asterisk to display next to heading labels that are only used for masters students
|
||||||
|
*/
|
||||||
|
const mastersOnlyFieldAsterisk = (
|
||||||
|
<span className="font-weight-normal">*</span>
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <UsernameLabelReplacement />
|
* <UsernameLabelReplacement />
|
||||||
* Username column header. Lists that Student Key is possibly available
|
* Username column header. Lists that Student Key is possibly available
|
||||||
@@ -56,11 +63,24 @@ const UsernameLabelReplacement = () => (
|
|||||||
</div>
|
</div>
|
||||||
<div className="font-weight-normal student-key">
|
<div className="font-weight-normal student-key">
|
||||||
<FormattedMessage {...messages.studentKeyLabel} />
|
<FormattedMessage {...messages.studentKeyLabel} />
|
||||||
|
{ mastersOnlyFieldAsterisk }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <MastersOnlyLabelReplacement {message}>
|
||||||
|
* Column header for fields that are only available for masters students
|
||||||
|
*/
|
||||||
|
const MastersOnlyLabelReplacement = (message) => (
|
||||||
|
<div>
|
||||||
|
<FormattedMessage {...message} />
|
||||||
|
{ mastersOnlyFieldAsterisk }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
export default StrictDict({
|
export default StrictDict({
|
||||||
TotalGradeLabelReplacement,
|
TotalGradeLabelReplacement,
|
||||||
UsernameLabelReplacement,
|
UsernameLabelReplacement,
|
||||||
|
MastersOnlyLabelReplacement,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import LabelReplacements from './LabelReplacements';
|
|||||||
const {
|
const {
|
||||||
TotalGradeLabelReplacement,
|
TotalGradeLabelReplacement,
|
||||||
UsernameLabelReplacement,
|
UsernameLabelReplacement,
|
||||||
|
MastersOnlyLabelReplacement,
|
||||||
} = LabelReplacements;
|
} = LabelReplacements;
|
||||||
|
|
||||||
jest.mock('@edx/paragon', () => ({
|
jest.mock('@edx/paragon', () => ({
|
||||||
@@ -35,6 +36,16 @@ describe('LabelReplacements', () => {
|
|||||||
expect(shallow(<UsernameLabelReplacement />)).toMatchSnapshot();
|
expect(shallow(<UsernameLabelReplacement />)).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('MastersOnlyLabelReplacement', () => {
|
||||||
|
test('snapshot', () => {
|
||||||
|
const message = {
|
||||||
|
id: 'id',
|
||||||
|
defaultMessage: 'defaultMessAge',
|
||||||
|
description: 'desCripTion',
|
||||||
|
};
|
||||||
|
expect(shallow(<MastersOnlyLabelReplacement {...message} />)).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('snapshot', () => {
|
describe('snapshot', () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`Gradebook Table Fields Email snapshot 1`] = `
|
exports[`Gradebook Table Fields Text snapshot 1`] = `
|
||||||
<span
|
<span
|
||||||
className="wrap-text-in-cell"
|
className="wrap-text-in-cell"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`LabelReplacements MastersOnlyLabelReplacement snapshot 1`] = `
|
||||||
|
<div>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="defaultMessAge"
|
||||||
|
description="desCripTion"
|
||||||
|
id="id"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="font-weight-normal"
|
||||||
|
>
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`LabelReplacements TotalGradeLabelReplacement displays overlay tooltip 1`] = `
|
exports[`LabelReplacements TotalGradeLabelReplacement displays overlay tooltip 1`] = `
|
||||||
<Tooltip
|
<Tooltip
|
||||||
id="course-grade-tooltip"
|
id="course-grade-tooltip"
|
||||||
@@ -73,10 +88,15 @@ exports[`LabelReplacements UsernameLabelReplacement snapshot 1`] = `
|
|||||||
className="font-weight-normal student-key"
|
className="font-weight-normal student-key"
|
||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
defaultMessage="Student Key*"
|
defaultMessage="Student Key"
|
||||||
description="Gradebook table Student Key label"
|
description="Gradebook table Student Key label"
|
||||||
id="gradebook.GradesView.table.labels.studentKey"
|
id="gradebook.GradesView.table.labels.studentKey"
|
||||||
/>
|
/>
|
||||||
|
<span
|
||||||
|
className="font-weight-normal"
|
||||||
|
>
|
||||||
|
*
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -13,8 +13,16 @@ exports[`GradebookTable component snapshot - fields1 and 2 between email and tot
|
|||||||
"accessor": "Username",
|
"accessor": "Username",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"Header": <FormattedMessage
|
"Header": <MastersOnlyLabelReplacement
|
||||||
defaultMessage="Email*"
|
defaultMessage="Full Name"
|
||||||
|
description="Gradebook table full name column header"
|
||||||
|
id="gradebook.GradesView.table.headings.fullName"
|
||||||
|
/>,
|
||||||
|
"accessor": "Full Name",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"Header": <MastersOnlyLabelReplacement
|
||||||
|
defaultMessage="Email"
|
||||||
description="Gradebook table email column header"
|
description="Gradebook table email column header"
|
||||||
id="gradebook.GradesView.table.headings.email"
|
id="gradebook.GradesView.table.headings.email"
|
||||||
/>,
|
/>,
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import PropTypes from 'prop-types';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { DataTable } from '@edx/paragon';
|
import { DataTable } from '@edx/paragon';
|
||||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||||
|
|
||||||
import selectors from 'data/selectors';
|
import selectors from 'data/selectors';
|
||||||
import { Headings } from 'data/constants/grades';
|
import { Headings } from 'data/constants/grades';
|
||||||
import { getLocalizedPercentSign } from 'i18n';
|
import { getLocalizedPercentSign } from 'i18n/utils';
|
||||||
|
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
import Fields from './Fields';
|
import Fields from './Fields';
|
||||||
@@ -38,7 +38,9 @@ export class GradebookTable extends React.Component {
|
|||||||
} else if (heading === Headings.username) {
|
} else if (heading === Headings.username) {
|
||||||
label = <LabelReplacements.UsernameLabelReplacement />;
|
label = <LabelReplacements.UsernameLabelReplacement />;
|
||||||
} else if (heading === Headings.email) {
|
} else if (heading === Headings.email) {
|
||||||
label = <FormattedMessage {...messages.emailHeading} />;
|
label = <LabelReplacements.MastersOnlyLabelReplacement {...messages.emailHeading} />;
|
||||||
|
} else if (heading === Headings.fullName) {
|
||||||
|
label = <LabelReplacements.MastersOnlyLabelReplacement {...messages.fullNameHeading} />;
|
||||||
} else {
|
} else {
|
||||||
label = heading;
|
label = heading;
|
||||||
}
|
}
|
||||||
@@ -49,7 +51,8 @@ export class GradebookTable extends React.Component {
|
|||||||
[Headings.username]: (
|
[Headings.username]: (
|
||||||
<Fields.Username username={entry.username} userKey={entry.external_user_key} />
|
<Fields.Username username={entry.username} userKey={entry.external_user_key} />
|
||||||
),
|
),
|
||||||
[Headings.email]: (<Fields.Email email={entry.email} />),
|
[Headings.fullName]: (<Fields.Text value={entry.full_name} />),
|
||||||
|
[Headings.email]: (<Fields.Text value={entry.email} />),
|
||||||
[Headings.totalGrade]: `${roundGrade(entry.percent * 100)}${getLocalizedPercentSign()}`,
|
[Headings.totalGrade]: `${roundGrade(entry.percent * 100)}${getLocalizedPercentSign()}`,
|
||||||
...entry.section_breakdown.reduce((acc, subsection) => ({
|
...entry.section_breakdown.reduce((acc, subsection) => ({
|
||||||
...acc,
|
...acc,
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
fullNameHeading: {
|
||||||
|
id: 'gradebook.GradesView.table.headings.fullName',
|
||||||
|
defaultMessage: 'Full Name',
|
||||||
|
description: 'Gradebook table full name column header',
|
||||||
|
},
|
||||||
emailHeading: {
|
emailHeading: {
|
||||||
id: 'gradebook.GradesView.table.headings.email',
|
id: 'gradebook.GradesView.table.headings.email',
|
||||||
defaultMessage: 'Email*',
|
defaultMessage: 'Email',
|
||||||
description: 'Gradebook table email column header',
|
description: 'Gradebook table email column header',
|
||||||
},
|
},
|
||||||
totalGradeHeading: {
|
totalGradeHeading: {
|
||||||
@@ -18,7 +23,7 @@ const messages = defineMessages({
|
|||||||
},
|
},
|
||||||
studentKeyLabel: {
|
studentKeyLabel: {
|
||||||
id: 'gradebook.GradesView.table.labels.studentKey',
|
id: 'gradebook.GradesView.table.labels.studentKey',
|
||||||
defaultMessage: 'Student Key*',
|
defaultMessage: 'Student Key',
|
||||||
description: 'Gradebook table Student Key label',
|
description: 'Gradebook table Student Key label',
|
||||||
},
|
},
|
||||||
usernameLabel: {
|
usernameLabel: {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import React from 'react';
|
|||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
|
|
||||||
import { DataTable } from '@edx/paragon';
|
import { DataTable } from '@edx/paragon';
|
||||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
|
||||||
|
|
||||||
import selectors from 'data/selectors';
|
import selectors from 'data/selectors';
|
||||||
import { Headings } from 'data/constants/grades';
|
import { Headings } from 'data/constants/grades';
|
||||||
@@ -22,7 +21,7 @@ jest.mock('./Fields', () => ({
|
|||||||
__esModule: true,
|
__esModule: true,
|
||||||
default: {
|
default: {
|
||||||
Username: () => 'Fields.Username',
|
Username: () => 'Fields.Username',
|
||||||
Email: () => 'Fields.Email',
|
Text: () => 'Fields.Text',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
jest.mock('./LabelReplacements', () => ({
|
jest.mock('./LabelReplacements', () => ({
|
||||||
@@ -30,6 +29,7 @@ jest.mock('./LabelReplacements', () => ({
|
|||||||
default: {
|
default: {
|
||||||
TotalGradeLabelReplacement: () => 'TotalGradeLabelReplacement',
|
TotalGradeLabelReplacement: () => 'TotalGradeLabelReplacement',
|
||||||
UsernameLabelReplacement: () => 'UsernameLabelReplacement',
|
UsernameLabelReplacement: () => 'UsernameLabelReplacement',
|
||||||
|
MastersOnlyLabelReplacement: () => 'MastersOnlyLabelReplacement',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
jest.mock('./GradeButton', () => 'GradeButton');
|
jest.mock('./GradeButton', () => 'GradeButton');
|
||||||
@@ -75,6 +75,7 @@ describe('GradebookTable', () => {
|
|||||||
],
|
],
|
||||||
headings: [
|
headings: [
|
||||||
Headings.username,
|
Headings.username,
|
||||||
|
Headings.fullName,
|
||||||
Headings.email,
|
Headings.email,
|
||||||
fields.field1,
|
fields.field1,
|
||||||
fields.field2,
|
fields.field2,
|
||||||
@@ -104,17 +105,22 @@ describe('GradebookTable', () => {
|
|||||||
expect(heading.accessor).toEqual(Headings.username);
|
expect(heading.accessor).toEqual(Headings.username);
|
||||||
expect(heading.Header.type).toEqual(LabelReplacements.UsernameLabelReplacement);
|
expect(heading.Header.type).toEqual(LabelReplacements.UsernameLabelReplacement);
|
||||||
});
|
});
|
||||||
test('email sets key and Header from header', () => {
|
test('full name sets key and Header from header', () => {
|
||||||
const heading = headings[1];
|
const heading = headings[1];
|
||||||
|
expect(heading.accessor).toEqual(Headings.fullName);
|
||||||
|
expect(heading.Header).toEqual(<LabelReplacements.MastersOnlyLabelReplacement {...messages.fullNameHeading} />);
|
||||||
|
});
|
||||||
|
test('email sets key and Header from header', () => {
|
||||||
|
const heading = headings[2];
|
||||||
expect(heading.accessor).toEqual(Headings.email);
|
expect(heading.accessor).toEqual(Headings.email);
|
||||||
expect(heading.Header).toEqual(<FormattedMessage {...messages.emailHeading} />);
|
expect(heading.Header).toEqual(<LabelReplacements.MastersOnlyLabelReplacement {...messages.emailHeading} />);
|
||||||
});
|
});
|
||||||
test('subsections set key and Header from header', () => {
|
test('subsections set key and Header from header', () => {
|
||||||
expect(headings[2]).toEqual({ accessor: fields.field1, Header: fields.field1 });
|
expect(headings[3]).toEqual({ accessor: fields.field1, Header: fields.field1 });
|
||||||
expect(headings[3]).toEqual({ accessor: fields.field2, Header: fields.field2 });
|
expect(headings[4]).toEqual({ accessor: fields.field2, Header: fields.field2 });
|
||||||
});
|
});
|
||||||
test('totalGrade sets key and replaces Header with component', () => {
|
test('totalGrade sets key and replaces Header with component', () => {
|
||||||
const heading = headings[4];
|
const heading = headings[5];
|
||||||
expect(heading.accessor).toEqual(Headings.totalGrade);
|
expect(heading.accessor).toEqual(Headings.totalGrade);
|
||||||
expect(heading.Header.type).toEqual(LabelReplacements.TotalGradeLabelReplacement);
|
expect(heading.Header.type).toEqual(LabelReplacements.TotalGradeLabelReplacement);
|
||||||
});
|
});
|
||||||
@@ -139,10 +145,15 @@ describe('GradebookTable', () => {
|
|||||||
userKey: entry.external_user_key,
|
userKey: entry.external_user_key,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
test('email set to Email Field', () => {
|
test('fullName set to Text Field', () => {
|
||||||
|
const field = row[Headings.fullName];
|
||||||
|
expect(field.type).toEqual(Fields.Text);
|
||||||
|
expect(field.props).toEqual({ value: entry.full_name });
|
||||||
|
});
|
||||||
|
test('email set to Text Field', () => {
|
||||||
const field = row[Headings.email];
|
const field = row[Headings.email];
|
||||||
expect(field.type).toEqual(Fields.Email);
|
expect(field.type).toEqual(Fields.Text);
|
||||||
expect(field.props).toEqual({ email: entry.email });
|
expect(field.props).toEqual({ value: entry.email });
|
||||||
});
|
});
|
||||||
test('totalGrade set to rounded percent grade * 100', () => {
|
test('totalGrade set to rounded percent grade * 100', () => {
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -46,7 +46,6 @@
|
|||||||
}
|
}
|
||||||
.grade-history-header{
|
.grade-history-header{
|
||||||
float: left;
|
float: left;
|
||||||
min-width: 170px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grade-history-assignment{
|
.grade-history-assignment{
|
||||||
@@ -66,7 +65,7 @@
|
|||||||
.gradebook-container {
|
.gradebook-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
max-height: 600px;
|
height: 600px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
@@ -123,34 +122,3 @@ select#ScoreView.form-control {
|
|||||||
border-right-color: $black;
|
border-right-color: $black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#edit-filters-btn {
|
|
||||||
@include media-breakpoint-down(xs) {
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
@include media-breakpoint-down(xs) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pgn__modal-body-content .pgn__data-table-layout-wrapper {
|
|
||||||
@include media-breakpoint-down(sm) {
|
|
||||||
clear: both;
|
|
||||||
padding: 1rem 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-gradebook {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.sidebar-container {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
aside.sidebar {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class SearchControls extends React.Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="search-container">
|
<div>
|
||||||
<SearchField
|
<SearchField
|
||||||
onSubmit={this.onSubmit}
|
onSubmit={this.onSubmit}
|
||||||
inputLabel={<FormattedMessage {...messages.label} />}
|
inputLabel={<FormattedMessage {...messages.label} />}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ exports[`FilterMenuToggle component snapshots basic snapshot 1`] = `
|
|||||||
onClick={[MockFunction this.props.toggleFilterDrawer]}
|
onClick={[MockFunction this.props.toggleFilterDrawer]}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
className="fa fa-filter mr-1"
|
className="fa fa-filter"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`SearchControls Component Snapshots basic snapshot 1`] = `
|
exports[`SearchControls Component Snapshots basic snapshot 1`] = `
|
||||||
<div
|
<div>
|
||||||
className="search-container"
|
|
||||||
>
|
|
||||||
<SearchField
|
<SearchField
|
||||||
inputLabel={
|
inputLabel={
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ exports[`GradesView Component snapshots basic snapshot 1`] = `
|
|||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
className="d-flex justify-content-between flex-wrap"
|
className="d-flex justify-content-between"
|
||||||
>
|
>
|
||||||
<FilterMenuToggle />
|
<FilterMenuToggle />
|
||||||
<SearchControls />
|
<SearchControls />
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export class GradesView extends React.Component {
|
|||||||
<FormattedMessage {...messages.filterStepHeading} />
|
<FormattedMessage {...messages.filterStepHeading} />
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<div className="d-flex justify-content-between flex-wrap">
|
<div className="d-flex justify-content-between">
|
||||||
<FilterMenuToggle />
|
<FilterMenuToggle />
|
||||||
<SearchControls />
|
<SearchControls />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { StrictDict } from 'utils';
|
|||||||
const EMAIL_HEADING = 'Email';
|
const EMAIL_HEADING = 'Email';
|
||||||
const TOTAL_COURSE_GRADE_HEADING = 'Total Grade (%)';
|
const TOTAL_COURSE_GRADE_HEADING = 'Total Grade (%)';
|
||||||
const USERNAME_HEADING = 'Username';
|
const USERNAME_HEADING = 'Username';
|
||||||
|
const FULL_NAME_HEADING = 'Full Name';
|
||||||
|
|
||||||
const GradeFormats = StrictDict({
|
const GradeFormats = StrictDict({
|
||||||
absolute: 'absolute',
|
absolute: 'absolute',
|
||||||
@@ -10,15 +11,17 @@ const GradeFormats = StrictDict({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const Headings = StrictDict({
|
const Headings = StrictDict({
|
||||||
email: 'Email',
|
email: EMAIL_HEADING,
|
||||||
totalGrade: 'Total Grade (%)',
|
totalGrade: TOTAL_COURSE_GRADE_HEADING,
|
||||||
username: 'Username',
|
username: USERNAME_HEADING,
|
||||||
|
fullName: FULL_NAME_HEADING,
|
||||||
});
|
});
|
||||||
|
|
||||||
export {
|
export {
|
||||||
EMAIL_HEADING,
|
EMAIL_HEADING,
|
||||||
TOTAL_COURSE_GRADE_HEADING,
|
TOTAL_COURSE_GRADE_HEADING,
|
||||||
USERNAME_HEADING,
|
USERNAME_HEADING,
|
||||||
|
FULL_NAME_HEADING,
|
||||||
GradeFormats,
|
GradeFormats,
|
||||||
Headings,
|
Headings,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { StrictDict } from 'utils';
|
|||||||
|
|
||||||
import { Headings, GradeFormats } from 'data/constants/grades';
|
import { Headings, GradeFormats } from 'data/constants/grades';
|
||||||
import { formatDateForDisplay } from 'data/actions/utils';
|
import { formatDateForDisplay } from 'data/actions/utils';
|
||||||
import { getLocalizedSlash } from 'i18n';
|
import { getLocalizedSlash } from 'i18n/utils';
|
||||||
import simpleSelectorFactory from '../utils';
|
import simpleSelectorFactory from '../utils';
|
||||||
import * as module from './grades';
|
import * as module from './grades';
|
||||||
|
|
||||||
@@ -105,12 +105,17 @@ export const headingMapper = (category, label = 'All') => {
|
|||||||
} else {
|
} else {
|
||||||
filter = filters.byLabel;
|
filter = filters.byLabel;
|
||||||
}
|
}
|
||||||
const { username, email, totalGrade } = Headings;
|
const {
|
||||||
|
username,
|
||||||
|
fullName,
|
||||||
|
email,
|
||||||
|
totalGrade,
|
||||||
|
} = Headings;
|
||||||
const filteredLabels = (entry) => entry.filter(filter).map(s => s.label);
|
const filteredLabels = (entry) => entry.filter(filter).map(s => s.label);
|
||||||
|
|
||||||
return (entry) => (
|
return (entry) => (
|
||||||
entry
|
entry
|
||||||
? [username, email, ...filteredLabels(entry), totalGrade]
|
? [username, fullName, email, ...filteredLabels(entry), totalGrade]
|
||||||
: []
|
: []
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { EMAIL_HEADING, TOTAL_COURSE_GRADE_HEADING, USERNAME_HEADING } from '../constants/grades';
|
import {
|
||||||
|
EMAIL_HEADING,
|
||||||
|
FULL_NAME_HEADING,
|
||||||
|
TOTAL_COURSE_GRADE_HEADING,
|
||||||
|
USERNAME_HEADING,
|
||||||
|
} from '../constants/grades';
|
||||||
import { formatDateForDisplay } from '../actions/utils';
|
import { formatDateForDisplay } from '../actions/utils';
|
||||||
import * as selectors from './grades';
|
import * as selectors from './grades';
|
||||||
import exportedSelectors from './grades';
|
import exportedSelectors from './grades';
|
||||||
@@ -179,6 +184,7 @@ describe('grades selectors', () => {
|
|||||||
describe('headingMapper', () => {
|
describe('headingMapper', () => {
|
||||||
const expectedHeaders = (subsectionLabels) => ([
|
const expectedHeaders = (subsectionLabels) => ([
|
||||||
USERNAME_HEADING,
|
USERNAME_HEADING,
|
||||||
|
FULL_NAME_HEADING,
|
||||||
EMAIL_HEADING,
|
EMAIL_HEADING,
|
||||||
...subsectionLabels,
|
...subsectionLabels,
|
||||||
TOTAL_COURSE_GRADE_HEADING,
|
TOTAL_COURSE_GRADE_HEADING,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { getLocale, isRtl } from '@edx/frontend-platform/i18n';
|
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||||
|
import { messages as headerMessages } from '@edx/frontend-component-header';
|
||||||
|
|
||||||
import arMessages from './messages/ar.json';
|
import arMessages from './messages/ar.json';
|
||||||
import deMessages from './messages/de.json';
|
import deMessages from './messages/de.json';
|
||||||
import es419Messages from './messages/es_419.json';
|
import es419Messages from './messages/es_419.json';
|
||||||
@@ -13,7 +15,7 @@ import ukMessages from './messages/uk.json';
|
|||||||
import zhcnMessages from './messages/zh_CN.json';
|
import zhcnMessages from './messages/zh_CN.json';
|
||||||
// no need to import en messages-- they are in the defaultMessage field
|
// no need to import en messages-- they are in the defaultMessage field
|
||||||
|
|
||||||
const messages = {
|
const appMessages = {
|
||||||
ar: arMessages,
|
ar: arMessages,
|
||||||
'es-419': es419Messages,
|
'es-419': es419Messages,
|
||||||
'fa-ir': faIRMessages,
|
'fa-ir': faIRMessages,
|
||||||
@@ -28,25 +30,8 @@ const messages = {
|
|||||||
uk: ukMessages,
|
uk: ukMessages,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLocalizedSlash = () => {
|
export default [
|
||||||
// For fractional grades
|
footerMessages,
|
||||||
// if we are in a LTR language, we want to use a forward slash.
|
headerMessages,
|
||||||
// If we are in a RTL language, we want to use a backslash instead
|
appMessages,
|
||||||
if (isRtl(getLocale())) {
|
];
|
||||||
return '\\';
|
|
||||||
}
|
|
||||||
return '/';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getLocalizedPercentSign = () => {
|
|
||||||
// LTR languages put the percent to the right of a number.
|
|
||||||
// RTL languages put the percent sign to the left of the number.
|
|
||||||
// We can place a non-printing unicode right-to-left marker next to the percent
|
|
||||||
// sign to make it print to the left of the number if we are currently in a LTR language
|
|
||||||
if (isRtl(getLocale())) {
|
|
||||||
return '\u200f%';
|
|
||||||
}
|
|
||||||
return '%';
|
|
||||||
};
|
|
||||||
|
|
||||||
export default messages;
|
|
||||||
22
src/i18n/utils.js
Normal file
22
src/i18n/utils.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { getLocale, isRtl } from '@edx/frontend-platform/i18n';
|
||||||
|
|
||||||
|
export const getLocalizedSlash = () => {
|
||||||
|
// For fractional grades
|
||||||
|
// if we are in a LTR language, we want to use a forward slash.
|
||||||
|
// If we are in a RTL language, we want to use a backslash instead
|
||||||
|
if (isRtl(getLocale())) {
|
||||||
|
return '\\';
|
||||||
|
}
|
||||||
|
return '/';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLocalizedPercentSign = () => {
|
||||||
|
// LTR languages put the percent to the right of a number.
|
||||||
|
// RTL languages put the percent sign to the left of the number.
|
||||||
|
// We can place a non-printing unicode right-to-left marker next to the percent
|
||||||
|
// sign to make it print to the left of the number if we are currently in a LTR language
|
||||||
|
if (isRtl(getLocale())) {
|
||||||
|
return '\u200f%';
|
||||||
|
}
|
||||||
|
return '%';
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { isRtl } from '@edx/frontend-platform/i18n';
|
import { isRtl } from '@edx/frontend-platform/i18n';
|
||||||
import { getLocalizedSlash, getLocalizedPercentSign } from './index';
|
import { getLocalizedSlash, getLocalizedPercentSign } from './utils';
|
||||||
|
|
||||||
jest.mock('@edx/frontend-platform/i18n', () => ({
|
jest.mock('@edx/frontend-platform/i18n', () => ({
|
||||||
isRtl: jest.fn(),
|
isRtl: jest.fn(),
|
||||||
@@ -10,13 +10,13 @@ import {
|
|||||||
mergeConfig,
|
mergeConfig,
|
||||||
subscribe,
|
subscribe,
|
||||||
} from '@edx/frontend-platform';
|
} from '@edx/frontend-platform';
|
||||||
import { messages as headerMessages } from '@edx/frontend-component-header';
|
import lightning from './lightning';
|
||||||
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
|
||||||
|
|
||||||
import appMessages from './i18n';
|
import messages from './i18n';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
subscribe(APP_READY, () => {
|
subscribe(APP_READY, () => {
|
||||||
|
lightning();
|
||||||
ReactDOM.render(<App />, document.getElementById('root'));
|
ReactDOM.render(<App />, document.getElementById('root'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,10 +37,6 @@ initialize({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
messages: [
|
messages,
|
||||||
appMessages,
|
|
||||||
headerMessages,
|
|
||||||
footerMessages,
|
|
||||||
],
|
|
||||||
requireAuthenticatedUser: true,
|
requireAuthenticatedUser: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import {
|
|||||||
mergeConfig,
|
mergeConfig,
|
||||||
subscribe,
|
subscribe,
|
||||||
} from '@edx/frontend-platform';
|
} from '@edx/frontend-platform';
|
||||||
import { messages as headerMessages } from '@edx/frontend-component-header';
|
|
||||||
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
|
||||||
|
|
||||||
import appMessages from './i18n';
|
import messages from './i18n';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import '.';
|
import '.';
|
||||||
|
|
||||||
@@ -23,12 +21,6 @@ jest.mock('@edx/frontend-platform', () => ({
|
|||||||
mergeConfig: jest.fn(),
|
mergeConfig: jest.fn(),
|
||||||
subscribe: jest.fn(),
|
subscribe: jest.fn(),
|
||||||
}));
|
}));
|
||||||
jest.mock('@edx/frontend-component-header', () => ({
|
|
||||||
messages: ['some', 'messages'],
|
|
||||||
}));
|
|
||||||
jest.mock('@edx/frontend-component-footer', () => ({
|
|
||||||
messages: ['some', 'messages'],
|
|
||||||
}));
|
|
||||||
jest.mock('./App', () => 'App');
|
jest.mock('./App', () => 'App');
|
||||||
|
|
||||||
describe('app registry', () => {
|
describe('app registry', () => {
|
||||||
@@ -50,7 +42,7 @@ describe('app registry', () => {
|
|||||||
});
|
});
|
||||||
test('initialize is called with requireAuthenticatedUser, messages, and a config handler', () => {
|
test('initialize is called with requireAuthenticatedUser, messages, and a config handler', () => {
|
||||||
expect(initialize).toHaveBeenCalledWith({
|
expect(initialize).toHaveBeenCalledWith({
|
||||||
messages: [appMessages, headerMessages, footerMessages],
|
messages,
|
||||||
requireAuthenticatedUser: true,
|
requireAuthenticatedUser: true,
|
||||||
handlers: {
|
handlers: {
|
||||||
config: expect.any(Function),
|
config: expect.any(Function),
|
||||||
|
|||||||
131
src/lightning.js
Normal file
131
src/lightning.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
module.exports = () => {
|
||||||
|
if (process.env.DISPLAY_FEEDBACK_WIDGET === "true") {
|
||||||
|
window.lightningjs ||
|
||||||
|
(function (n) {
|
||||||
|
var e = "lightningjs";
|
||||||
|
function t(e, t) {
|
||||||
|
var r, i, a, o, d, c;
|
||||||
|
return (
|
||||||
|
t && (t += (/\?/.test(t) ? "&" : "?") + "lv=1"),
|
||||||
|
n[e] ||
|
||||||
|
((r = window),
|
||||||
|
(i = document),
|
||||||
|
(a = e),
|
||||||
|
(o = i.location.protocol),
|
||||||
|
(d = "load"),
|
||||||
|
(c = 0),
|
||||||
|
(function () {
|
||||||
|
n[a] = function () {
|
||||||
|
var t = arguments,
|
||||||
|
i = this,
|
||||||
|
o = ++c,
|
||||||
|
d = (i && i != r && i.id) || 0;
|
||||||
|
function s() {
|
||||||
|
return (s.id = o), n[a].apply(s, arguments);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
(e.s = e.s || []).push([o, d, t]),
|
||||||
|
(s.then = function (n, t, r) {
|
||||||
|
var i = (e.fh[o] = e.fh[o] || []),
|
||||||
|
a = (e.eh[o] = e.eh[o] || []),
|
||||||
|
d = (e.ph[o] = e.ph[o] || []);
|
||||||
|
return n && i.push(n), t && a.push(t), r && d.push(r), s;
|
||||||
|
}),
|
||||||
|
s
|
||||||
|
);
|
||||||
|
};
|
||||||
|
var e = (n[a]._ = {});
|
||||||
|
function s() {
|
||||||
|
e.P(d), (e.w = 1), n[a]("_load");
|
||||||
|
}
|
||||||
|
(e.fh = {}),
|
||||||
|
(e.eh = {}),
|
||||||
|
(e.ph = {}),
|
||||||
|
(e.l = t
|
||||||
|
? t.replace(/^\/\//, ("https:" == o ? o : "http:") + "//")
|
||||||
|
: t),
|
||||||
|
(e.p = { 0: +new Date() }),
|
||||||
|
(e.P = function (n) {
|
||||||
|
e.p[n] = new Date() - e.p[0];
|
||||||
|
}),
|
||||||
|
e.w && s(),
|
||||||
|
r.addEventListener
|
||||||
|
? r.addEventListener(d, s, !1)
|
||||||
|
: r.attachEvent("onload", s);
|
||||||
|
var l = function () {
|
||||||
|
function n() {
|
||||||
|
return [
|
||||||
|
"<!DOCTYPE ",
|
||||||
|
o,
|
||||||
|
"><",
|
||||||
|
o,
|
||||||
|
"><head></head><",
|
||||||
|
t,
|
||||||
|
"><",
|
||||||
|
r,
|
||||||
|
' src="',
|
||||||
|
e.l,
|
||||||
|
'"></',
|
||||||
|
r,
|
||||||
|
"></",
|
||||||
|
t,
|
||||||
|
"></",
|
||||||
|
o,
|
||||||
|
">",
|
||||||
|
].join("");
|
||||||
|
}
|
||||||
|
var t = "body",
|
||||||
|
r = "script",
|
||||||
|
o = "html",
|
||||||
|
d = i[t];
|
||||||
|
if (!d) return setTimeout(l, 100);
|
||||||
|
e.P(1);
|
||||||
|
var c,
|
||||||
|
s = i.createElement("div"),
|
||||||
|
h = s.appendChild(i.createElement("div")),
|
||||||
|
u = i.createElement("iframe");
|
||||||
|
(s.style.display = "none"),
|
||||||
|
(d.insertBefore(s, d.firstChild).id = "lightningjs-" + a),
|
||||||
|
(u.frameBorder = "0"),
|
||||||
|
(u.id = "lightningjs-frame-" + a),
|
||||||
|
/MSIE[ ]+6/.test(navigator.userAgent) &&
|
||||||
|
(u.src = "javascript:false"),
|
||||||
|
(u.allowTransparency = "true"),
|
||||||
|
h.appendChild(u);
|
||||||
|
try {
|
||||||
|
u.contentWindow.document.open();
|
||||||
|
} catch (n) {
|
||||||
|
(e.domain = i.domain),
|
||||||
|
(c =
|
||||||
|
"javascript:var d=document.open();d.domain='" +
|
||||||
|
i.domain +
|
||||||
|
"';"),
|
||||||
|
(u.src = c + "void(0);");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
var p = u.contentWindow.document;
|
||||||
|
p.write(n()), p.close();
|
||||||
|
} catch (e) {
|
||||||
|
u.src =
|
||||||
|
c +
|
||||||
|
'd.write("' +
|
||||||
|
n().replace(/"/g, String.fromCharCode(92) + '"') +
|
||||||
|
'");d.close();';
|
||||||
|
}
|
||||||
|
e.P(2);
|
||||||
|
};
|
||||||
|
e.l && l();
|
||||||
|
})()),
|
||||||
|
(n[e].lv = "1"),
|
||||||
|
n[e]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var r = (window.lightningjs = t(e));
|
||||||
|
(r.require = t), (r.modules = n);
|
||||||
|
})({});
|
||||||
|
window.usabilla_live = lightningjs.require(
|
||||||
|
"usabilla_live",
|
||||||
|
"//w.usabilla.com/a13a22d8ea20.js"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -32,6 +32,13 @@ jest.mock('@edx/frontend-platform/i18n', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('@edx/frontend-component-header', () => ({
|
||||||
|
messages: ['some', 'messages'],
|
||||||
|
}));
|
||||||
|
jest.mock('@edx/frontend-component-footer', () => ({
|
||||||
|
messages: ['some', 'messages'],
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedComponents({
|
jest.mock('@edx/paragon', () => jest.requireActual('testUtils').mockNestedComponents({
|
||||||
Alert: 'Alert',
|
Alert: 'Alert',
|
||||||
ActionRow: 'ActionRow',
|
ActionRow: 'ActionRow',
|
||||||
|
|||||||
Reference in New Issue
Block a user