rebase against next
This commit is contained in:
@ -3,7 +3,8 @@
|
|||||||
"es6": true,
|
"es6": true,
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"mocha": true,
|
"mocha": true,
|
||||||
"node": true
|
"node": true,
|
||||||
|
"jest": true
|
||||||
},
|
},
|
||||||
"parser": "babel-eslint",
|
"parser": "babel-eslint",
|
||||||
"root": true,
|
"root": true,
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -179,3 +179,6 @@ api-server/lib/*
|
|||||||
curriculum/dist
|
curriculum/dist
|
||||||
curriculum/build
|
curriculum/build
|
||||||
client/static/_redirects
|
client/static/_redirects
|
||||||
|
|
||||||
|
### UI Components ###
|
||||||
|
tools/ui-components/dist
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import { ensureLowerCaseEmail } from './User-Identity';
|
import { ensureLowerCaseEmail } from './User-Identity';
|
||||||
|
|
||||||
test('returns lowercase email when one exists', () => {
|
test('returns lowercase email when one exists', () => {
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
renderSignInEmail
|
renderSignInEmail
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
|
|
||||||
import { blocklistedUsernames } from '../../server/utils/constants.js';
|
import { blocklistedUsernames } from '../../../../config/constants';
|
||||||
import { wrapHandledError } from '../../server/utils/create-handled-error.js';
|
import { wrapHandledError } from '../../server/utils/create-handled-error.js';
|
||||||
import { saveUser, observeMethod } from '../../server/utils/rx.js';
|
import { saveUser, observeMethod } from '../../server/utils/rx.js';
|
||||||
import { getEmailSender } from '../../server/utils/url-utils';
|
import { getEmailSender } from '../../server/utils/url-utils';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global it expect */
|
|
||||||
|
|
||||||
import { getFallbackFrontEndDate } from '../boot/certificate';
|
import { getFallbackFrontEndDate } from '../boot/certificate';
|
||||||
import { fullStackChallenges } from './fixtures';
|
import { fullStackChallenges } from './fixtures';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe xdescribe it expect jest */
|
|
||||||
import { first, find } from 'lodash';
|
import { first, find } from 'lodash';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global jest*/
|
|
||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { isEmail } from 'validator';
|
import { isEmail } from 'validator';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe it expect jest */
|
|
||||||
import { mockReq as mockRequest, mockRes } from '../boot_tests/challenge.test';
|
import { mockReq as mockRequest, mockRes } from '../boot_tests/challenge.test';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe expect it */
|
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
|
|
||||||
import { dayCount } from './date-utils';
|
import { dayCount } from './date-utils';
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
/* global describe it expect */
|
|
||||||
/* global jest*/
|
|
||||||
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import keys from '../../../../config/secrets';
|
import keys from '../../../../config/secrets';
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe it expect */
|
|
||||||
import {
|
import {
|
||||||
getAccessTokenFromRequest,
|
getAccessTokenFromRequest,
|
||||||
errorTypes,
|
errorTypes,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe beforeEach expect it jest */
|
|
||||||
import inMemoryCache from './in-memory-cache';
|
import inMemoryCache from './in-memory-cache';
|
||||||
|
|
||||||
describe('InMemoryCache', () => {
|
describe('InMemoryCache', () => {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global describe expect it jest */
|
|
||||||
|
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
const { getReturnTo, normalizeParams } = require('./redirection');
|
const { getReturnTo, normalizeParams } = require('./redirection');
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe it expect jest */
|
|
||||||
import moment from 'moment-timezone';
|
import moment from 'moment-timezone';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import {
|
import {
|
||||||
availableLangs,
|
availableLangs,
|
||||||
langDisplayNames,
|
langDisplayNames,
|
||||||
|
@ -297,15 +297,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relational-databases": {
|
"relational-databases": {
|
||||||
"title": "Relational Databases",
|
"title": "關係型數據庫",
|
||||||
"intro": [
|
"intro": [
|
||||||
"placeholder"
|
"佔位符"
|
||||||
],
|
],
|
||||||
"blocks": {
|
"blocks": {
|
||||||
"learn-relational-databases": {
|
"learn-relational-databases": {
|
||||||
"title": "Learn Relational Databases",
|
"title": "學習關係型數據庫",
|
||||||
"intro": [
|
"intro": [
|
||||||
"placeholder"
|
"佔位符"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,8 @@
|
|||||||
"analytics": "條形圖和折線圖",
|
"analytics": "條形圖和折線圖",
|
||||||
"shield": "帶有複選標記的盾牌",
|
"shield": "帶有複選標記的盾牌",
|
||||||
"tensorflow": "Tensorflow 圖標",
|
"tensorflow": "Tensorflow 圖標",
|
||||||
"algorithm": "分支節點"
|
"algorithm": "分支節點",
|
||||||
|
"magnifier": "magnifier"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"fcc-logo": "freeCodeCamp 徽標",
|
"fcc-logo": "freeCodeCamp 徽標",
|
||||||
|
@ -297,15 +297,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"relational-databases": {
|
"relational-databases": {
|
||||||
"title": "Relational Databases",
|
"title": "关系型数据库",
|
||||||
"intro": [
|
"intro": [
|
||||||
"placeholder"
|
"占位符"
|
||||||
],
|
],
|
||||||
"blocks": {
|
"blocks": {
|
||||||
"learn-relational-databases": {
|
"learn-relational-databases": {
|
||||||
"title": "Learn Relational Databases",
|
"title": "学习关系型数据库",
|
||||||
"intro": [
|
"intro": [
|
||||||
"placeholder"
|
"占位符"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +395,8 @@
|
|||||||
"analytics": "条形图和折线图",
|
"analytics": "条形图和折线图",
|
||||||
"shield": "带有复选标记的盾牌",
|
"shield": "带有复选标记的盾牌",
|
||||||
"tensorflow": "Tensorflow 图标",
|
"tensorflow": "Tensorflow 图标",
|
||||||
"algorithm": "分支节点"
|
"algorithm": "分支节点",
|
||||||
|
"magnifier": "magnifier"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"fcc-logo": "freeCodeCamp 徽标",
|
"fcc-logo": "freeCodeCamp 徽标",
|
||||||
|
@ -395,7 +395,8 @@
|
|||||||
"analytics": "A bar chart and line graph",
|
"analytics": "A bar chart and line graph",
|
||||||
"shield": "A shield with a checkmark",
|
"shield": "A shield with a checkmark",
|
||||||
"tensorflow": "Tensorflow icon",
|
"tensorflow": "Tensorflow icon",
|
||||||
"algorithm": "Branching nodes"
|
"algorithm": "Branching nodes",
|
||||||
|
"magnifier": "magnifier"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"fcc-logo": "freeCodeCamp Logo",
|
"fcc-logo": "freeCodeCamp Logo",
|
||||||
|
@ -395,7 +395,8 @@
|
|||||||
"analytics": "Un gráfico de barras y un gráfico de líneas",
|
"analytics": "Un gráfico de barras y un gráfico de líneas",
|
||||||
"shield": "Un escudo con una marca de verificación",
|
"shield": "Un escudo con una marca de verificación",
|
||||||
"tensorflow": "Icono de Tensorflow",
|
"tensorflow": "Icono de Tensorflow",
|
||||||
"algorithm": "Nodos ramificados"
|
"algorithm": "Nodos ramificados",
|
||||||
|
"magnifier": "magnifier"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"fcc-logo": "Logo de freeCodeCamp",
|
"fcc-logo": "Logo de freeCodeCamp",
|
||||||
|
@ -395,7 +395,8 @@
|
|||||||
"analytics": "Un grafico a barre e un grafico a linea",
|
"analytics": "Un grafico a barre e un grafico a linea",
|
||||||
"shield": "Uno scudo con un segno di spunta",
|
"shield": "Uno scudo con un segno di spunta",
|
||||||
"tensorflow": "Icona di Tensorflow",
|
"tensorflow": "Icona di Tensorflow",
|
||||||
"algorithm": "Nodi con rami"
|
"algorithm": "Nodi con rami",
|
||||||
|
"magnifier": "magnifier"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"fcc-logo": "Logo freeCodeCamp",
|
"fcc-logo": "Logo freeCodeCamp",
|
||||||
@ -529,7 +530,7 @@
|
|||||||
"Caesars Cipher": "Cifrario di Cesare",
|
"Caesars Cipher": "Cifrario di Cesare",
|
||||||
"Telephone Number Validator": "Validatore Numero Di Telefono",
|
"Telephone Number Validator": "Validatore Numero Di Telefono",
|
||||||
"Cash Register": "Registro dei Contanti",
|
"Cash Register": "Registro dei Contanti",
|
||||||
"Build a Drum Machine": "Costruisci una macchina a tamburo",
|
"Build a Drum Machine": "Costruisci una batteria",
|
||||||
"Visualize Data with a Choropleth Map": "Visualizza i dati con una mappa a colori Coropletica",
|
"Visualize Data with a Choropleth Map": "Visualizza i dati con una mappa a colori Coropletica",
|
||||||
"Visualize Data with a Treemap Diagram": "Visualizza i dati con una mappa ad albero",
|
"Visualize Data with a Treemap Diagram": "Visualizza i dati con una mappa ad albero",
|
||||||
"Exercise Tracker": "Monitor degli esercizi",
|
"Exercise Tracker": "Monitor degli esercizi",
|
||||||
|
@ -395,7 +395,8 @@
|
|||||||
"analytics": "A bar chart and line graph",
|
"analytics": "A bar chart and line graph",
|
||||||
"shield": "A shield with a checkmark",
|
"shield": "A shield with a checkmark",
|
||||||
"tensorflow": "Tensorflow icon",
|
"tensorflow": "Tensorflow icon",
|
||||||
"algorithm": "Branching nodes"
|
"algorithm": "Branching nodes",
|
||||||
|
"magnifier": "magnifier"
|
||||||
},
|
},
|
||||||
"aria": {
|
"aria": {
|
||||||
"fcc-logo": "freeCodeCamp Logo",
|
"fcc-logo": "freeCodeCamp Logo",
|
||||||
|
2589
client/package-lock.json
generated
2589
client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -39,9 +39,9 @@
|
|||||||
"@babel/plugin-proposal-export-default-from": "7.14.5",
|
"@babel/plugin-proposal-export-default-from": "7.14.5",
|
||||||
"@babel/plugin-proposal-function-bind": "7.14.5",
|
"@babel/plugin-proposal-function-bind": "7.14.5",
|
||||||
"@babel/polyfill": "7.12.1",
|
"@babel/polyfill": "7.12.1",
|
||||||
"@babel/preset-env": "7.14.5",
|
"@babel/preset-env": "7.14.7",
|
||||||
"@babel/preset-react": "7.14.5",
|
"@babel/preset-react": "7.14.5",
|
||||||
"@babel/standalone": "7.14.6",
|
"@babel/standalone": "7.14.7",
|
||||||
"@fortawesome/fontawesome": "1.1.8",
|
"@fortawesome/fontawesome": "1.1.8",
|
||||||
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
||||||
"@fortawesome/free-brands-svg-icons": "5.15.3",
|
"@fortawesome/free-brands-svg-icons": "5.15.3",
|
||||||
@ -70,17 +70,17 @@
|
|||||||
"enzyme": "3.11.0",
|
"enzyme": "3.11.0",
|
||||||
"enzyme-adapter-react-16": "1.15.6",
|
"enzyme-adapter-react-16": "1.15.6",
|
||||||
"final-form": "4.20.2",
|
"final-form": "4.20.2",
|
||||||
"gatsby": "3.7.2",
|
"gatsby": "3.8.0",
|
||||||
"gatsby-cli": "3.7.1",
|
"gatsby-cli": "3.8.0",
|
||||||
"gatsby-plugin-advanced-sitemap": "1.6.0",
|
"gatsby-plugin-advanced-sitemap": "1.6.0",
|
||||||
"gatsby-plugin-create-client-paths": "3.7.1",
|
"gatsby-plugin-create-client-paths": "3.8.0",
|
||||||
"gatsby-plugin-manifest": "3.7.1",
|
"gatsby-plugin-manifest": "3.8.0",
|
||||||
"gatsby-plugin-postcss": "4.7.1",
|
"gatsby-plugin-postcss": "4.8.0",
|
||||||
"gatsby-plugin-react-helmet": "4.7.1",
|
"gatsby-plugin-react-helmet": "4.8.0",
|
||||||
"gatsby-plugin-remove-serviceworker": "1.0.0",
|
"gatsby-plugin-remove-serviceworker": "1.0.0",
|
||||||
"gatsby-remark-prismjs": "5.4.1",
|
"gatsby-remark-prismjs": "5.5.0",
|
||||||
"gatsby-source-filesystem": "3.7.1",
|
"gatsby-source-filesystem": "3.8.0",
|
||||||
"gatsby-transformer-remark": "4.4.1",
|
"gatsby-transformer-remark": "4.5.0",
|
||||||
"i18next": "20.3.2",
|
"i18next": "20.3.2",
|
||||||
"jquery": "3.6.0",
|
"jquery": "3.6.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
@ -94,7 +94,7 @@
|
|||||||
"process": "0.11.10",
|
"process": "0.11.10",
|
||||||
"prop-types": "15.7.2",
|
"prop-types": "15.7.2",
|
||||||
"psl": "1.8.0",
|
"psl": "1.8.0",
|
||||||
"query-string": "7.0.0",
|
"query-string": "7.0.1",
|
||||||
"react": "16.14.0",
|
"react": "16.14.0",
|
||||||
"react-dom": "16.14.0",
|
"react-dom": "16.14.0",
|
||||||
"react-final-form": "6.5.3",
|
"react-final-form": "6.5.3",
|
||||||
@ -130,22 +130,36 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/types": "7.14.5",
|
"@babel/types": "7.14.5",
|
||||||
"@codesee/babel-plugin-instrument": "0.38.0",
|
"@codesee/babel-plugin-instrument": "0.41.0",
|
||||||
"@codesee/tracker": "0.38.0",
|
"@codesee/tracker": "0.41.0",
|
||||||
"@testing-library/jest-dom": "5.14.1",
|
"@testing-library/jest-dom": "5.14.1",
|
||||||
"@testing-library/react": "11.2.7",
|
"@testing-library/react": "11.2.7",
|
||||||
|
"@types/jest": "^26.0.23",
|
||||||
|
"@types/loadable__component": "^5.13.3",
|
||||||
|
"@types/lodash-es": "^4.17.4",
|
||||||
|
"@types/react-dom": "^17.0.8",
|
||||||
|
"@types/react-helmet": "^6.1.1",
|
||||||
|
"@types/react-instantsearch-dom": "^6.10.0",
|
||||||
|
"@types/react-monaco-editor": "^0.16.0",
|
||||||
|
"@types/react-redux": "^7.1.16",
|
||||||
|
"@types/react-responsive": "^8.0.2",
|
||||||
|
"@types/react-spinkit": "^3.0.6",
|
||||||
|
"@types/react-test-renderer": "^17.0.1",
|
||||||
"@types/react-transition-group": "4.4.1",
|
"@types/react-transition-group": "4.4.1",
|
||||||
"@types/redux-actions": "2.6.1",
|
"@types/redux-actions": "2.6.1",
|
||||||
|
"@types/sanitize-html": "^2.3.1",
|
||||||
|
"@types/store": "^2.0.2",
|
||||||
|
"@types/validator": "^13.1.4",
|
||||||
"autoprefixer": "10.2.6",
|
"autoprefixer": "10.2.6",
|
||||||
"babel-plugin-transform-imports": "2.0.0",
|
"babel-plugin-transform-imports": "2.0.0",
|
||||||
"chokidar": "3.5.2",
|
"chokidar": "3.5.2",
|
||||||
"copy-webpack-plugin": "9.0.0",
|
"copy-webpack-plugin": "9.0.0",
|
||||||
"gatsby-plugin-webpack-bundle-analyser-v2": "1.1.22",
|
"gatsby-plugin-webpack-bundle-analyser-v2": "1.1.24",
|
||||||
"jest-json-schema-extended": "1.0.0",
|
"jest-json-schema-extended": "1.0.0",
|
||||||
"monaco-editor-webpack-plugin": "4.0.0",
|
"monaco-editor-webpack-plugin": "4.0.0",
|
||||||
"react-test-renderer": "16.14.0",
|
"react-test-renderer": "16.14.0",
|
||||||
"redux-saga-test-plan": "4.0.1",
|
"redux-saga-test-plan": "4.0.1",
|
||||||
"webpack": "5.39.1",
|
"webpack": "5.40.0",
|
||||||
"webpack-cli": "4.7.2"
|
"webpack-cli": "4.7.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
client/src/__mocks__/react-i18nextMock.js
vendored
1
client/src/__mocks__/react-i18nextMock.js
vendored
@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable react/prop-types */
|
/* eslint-disable react/prop-types */
|
||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
/* global jest */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const reactI18next = jest.genMockFromModule('react-i18next');
|
const reactI18next = jest.genMockFromModule('react-i18next');
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe it expect */
|
|
||||||
import {
|
import {
|
||||||
wrapHandledError,
|
wrapHandledError,
|
||||||
unwrapHandledError
|
unwrapHandledError
|
||||||
|
24
client/src/assets/icons/Magnifier.tsx
Normal file
24
client/src/assets/icons/Magnifier.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const Magnifier = (): JSX.Element => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<span className='sr-only'>{t('icons.Magnifier')}</span>
|
||||||
|
<svg
|
||||||
|
className='ais-SearchBox-submitIcon'
|
||||||
|
height='10'
|
||||||
|
viewBox='0 0 40 40'
|
||||||
|
width='10'
|
||||||
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
|
>
|
||||||
|
<path d='M26.804 29.01c-2.832 2.34-6.465 3.746-10.426 3.746C7.333 32.756 0 25.424 0 16.378 0 7.333 7.333 0 16.378 0c9.046 0 16.378 7.333 16.378 16.378 0 3.96-1.406 7.594-3.746 10.426l10.534 10.534c.607.607.61 1.59-.004 2.202-.61.61-1.597.61-2.202.004L26.804 29.01zm-10.426.627c7.323 0 13.26-5.936 13.26-13.26 0-7.32-5.937-13.257-13.26-13.257C9.056 3.12 3.12 9.056 3.12 16.378c0 7.323 5.936 13.26 13.258 13.26z' />
|
||||||
|
</svg>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Magnifier.displayName = 'Magnifier';
|
||||||
|
export default Magnifier;
|
@ -1,4 +1,3 @@
|
|||||||
/* global jest, expect */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
import envData from '../../../config/env.json';
|
import envData from '../../../config/env.json';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
|
|
||||||
|
@ -3,10 +3,17 @@ import PropTypes from 'prop-types';
|
|||||||
|
|
||||||
import { Link, SkeletonSprite } from '../../helpers';
|
import { Link, SkeletonSprite } from '../../helpers';
|
||||||
import NavLogo from './NavLogo';
|
import NavLogo from './NavLogo';
|
||||||
import SearchBar from '../../search/searchBar/SearchBar';
|
|
||||||
import MenuButton from './MenuButton';
|
import MenuButton from './MenuButton';
|
||||||
import NavLinks from './NavLinks';
|
import NavLinks from './NavLinks';
|
||||||
import './universalNav.css';
|
import './universalNav.css';
|
||||||
|
import { isLanding } from '../../../utils/path-parsers';
|
||||||
|
|
||||||
|
import Loadable from '@loadable/component';
|
||||||
|
|
||||||
|
const SearchBar = Loadable(() => import('../../search/searchBar/SearchBar'));
|
||||||
|
const SearchBarOptimized = Loadable(() =>
|
||||||
|
import('../../search/searchBar/search-bar-optimized')
|
||||||
|
);
|
||||||
|
|
||||||
export const UniversalNav = ({
|
export const UniversalNav = ({
|
||||||
displayMenu,
|
displayMenu,
|
||||||
@ -17,6 +24,14 @@ export const UniversalNav = ({
|
|||||||
fetchState
|
fetchState
|
||||||
}) => {
|
}) => {
|
||||||
const { pending } = fetchState;
|
const { pending } = fetchState;
|
||||||
|
|
||||||
|
const search =
|
||||||
|
typeof window !== `undefined` && isLanding(window.location.pathname) ? (
|
||||||
|
<SearchBarOptimized />
|
||||||
|
) : (
|
||||||
|
<SearchBar innerRef={searchBarRef} />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
className={'universal-nav' + (displayMenu ? ' expand-nav' : '')}
|
className={'universal-nav' + (displayMenu ? ' expand-nav' : '')}
|
||||||
@ -27,7 +42,7 @@ export const UniversalNav = ({
|
|||||||
'universal-nav-left' + (displayMenu ? ' display-search' : '')
|
'universal-nav-left' + (displayMenu ? ' display-search' : '')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SearchBar innerRef={searchBarRef} />
|
{search}
|
||||||
</div>
|
</div>
|
||||||
<div className='universal-nav-middle'>
|
<div className='universal-nav-middle'>
|
||||||
<Link id='universal-nav-logo' to='/learn'>
|
<Link id='universal-nav-logo' to='/learn'>
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useStaticQuery } from 'gatsby';
|
import { useStaticQuery } from 'gatsby';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import createExternalRedirect from './createExternalRedirects';
|
import createExternalRedirect from './createExternalRedirects';
|
||||||
|
|
||||||
describe('createExternalRedirects', () => {
|
describe('createExternalRedirects', () => {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import createLanguageRedirect from './createLanguageRedirect';
|
import createLanguageRedirect from './createLanguageRedirect';
|
||||||
|
|
||||||
describe('createLanguageRedirect for clientLocale === english', () => {
|
describe('createLanguageRedirect for clientLocale === english', () => {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global jest, expect */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, fireEvent } from '@testing-library/react';
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import renderer, { ReactTestRendererJSON } from 'react-test-renderer';
|
import renderer, { ReactTestRendererJSON } from 'react-test-renderer';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, cleanup } from '@testing-library/react';
|
import { render, cleanup } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import { flashMessageSelector, removeFlashMessage } from '../Flash/redux';
|
|||||||
|
|
||||||
import { isBrowser } from '../../../utils';
|
import { isBrowser } from '../../../utils';
|
||||||
|
|
||||||
import WithInstantSearch from '../search/WithInstantSearch';
|
|
||||||
import OfflineWarning from '../OfflineWarning';
|
import OfflineWarning from '../OfflineWarning';
|
||||||
import Flash from '../Flash';
|
import Flash from '../Flash';
|
||||||
import Header from '../Header';
|
import Header from '../Header';
|
||||||
@ -201,17 +200,15 @@ class DefaultLayout extends Component {
|
|||||||
/>
|
/>
|
||||||
<style>{fontawesome.dom.css()}</style>
|
<style>{fontawesome.dom.css()}</style>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<WithInstantSearch>
|
<div className={`default-layout`}>
|
||||||
<div className={`default-layout`}>
|
<Header fetchState={fetchState} user={user} />
|
||||||
<Header fetchState={fetchState} user={user} />
|
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
{hasMessage && flashMessage ? (
|
||||||
{hasMessage && flashMessage ? (
|
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
||||||
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
) : null}
|
||||||
) : null}
|
{children}
|
||||||
{children}
|
</div>
|
||||||
</div>
|
{showFooter && <Footer />}
|
||||||
{showFooter && <Footer />}
|
|
||||||
</WithInstantSearch>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import TimeLine from './TimeLine';
|
import TimeLine from './TimeLine';
|
||||||
|
@ -9,6 +9,8 @@ import { isEqual } from 'lodash-es';
|
|||||||
import { withTranslation } from 'react-i18next';
|
import { withTranslation } from 'react-i18next';
|
||||||
import { searchPageUrl } from '../../../utils/algolia-locale-setup';
|
import { searchPageUrl } from '../../../utils/algolia-locale-setup';
|
||||||
|
|
||||||
|
import WithInstantSearch from '../WithInstantSearch';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isSearchDropdownEnabledSelector,
|
isSearchDropdownEnabledSelector,
|
||||||
isSearchBarFocusedSelector,
|
isSearchBarFocusedSelector,
|
||||||
@ -177,33 +179,39 @@ export class SearchBar extends Component {
|
|||||||
const placeholder = t('search.placeholder');
|
const placeholder = t('search.placeholder');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='fcc_searchBar' data-testid='fcc_searchBar' ref={innerRef}>
|
<WithInstantSearch>
|
||||||
<HotKeys handlers={this.keyHandlers} keyMap={this.keyMap}>
|
<div
|
||||||
<div className='fcc_search_wrapper'>
|
className='fcc_searchBar'
|
||||||
<label className='fcc_sr_only' htmlFor='fcc_instantsearch'>
|
data-testid='fcc_searchBar'
|
||||||
{t('search.label')}
|
ref={innerRef}
|
||||||
</label>
|
>
|
||||||
<ObserveKeys except={['Space']}>
|
<HotKeys handlers={this.keyHandlers} keyMap={this.keyMap}>
|
||||||
<SearchBox
|
<div className='fcc_search_wrapper'>
|
||||||
focusShortcuts={[83, 191]}
|
<label className='fcc_sr_only' htmlFor='fcc_instantsearch'>
|
||||||
onChange={this.handleChange}
|
{t('search.label')}
|
||||||
onFocus={this.handleFocus}
|
</label>
|
||||||
onSubmit={this.handleSearch}
|
<ObserveKeys except={['Space']}>
|
||||||
showLoadingIndicator={false}
|
<SearchBox
|
||||||
translations={{ placeholder }}
|
focusShortcuts={[83, 191]}
|
||||||
/>
|
onChange={this.handleChange}
|
||||||
</ObserveKeys>
|
onFocus={this.handleFocus}
|
||||||
{isDropdownEnabled && isSearchFocused && (
|
onSubmit={this.handleSearch}
|
||||||
<SearchHits
|
showLoadingIndicator={false}
|
||||||
handleHits={this.handleHits}
|
translations={{ placeholder }}
|
||||||
handleMouseEnter={this.handleMouseEnter}
|
/>
|
||||||
handleMouseLeave={this.handleMouseLeave}
|
</ObserveKeys>
|
||||||
selectedIndex={index}
|
{isDropdownEnabled && isSearchFocused && (
|
||||||
/>
|
<SearchHits
|
||||||
)}
|
handleHits={this.handleHits}
|
||||||
</div>
|
handleMouseEnter={this.handleMouseEnter}
|
||||||
</HotKeys>
|
handleMouseLeave={this.handleMouseLeave}
|
||||||
</div>
|
selectedIndex={index}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</HotKeys>
|
||||||
|
</div>
|
||||||
|
</WithInstantSearch>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global jest, expect */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ const CustomHits = connectHits(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='ais-Hits'>
|
<div className='ais-Hits'>
|
||||||
<ul className='ais-Hits-list'>
|
<ul className='ais-Hits-list' data-cy='ais-Hits-list'>
|
||||||
{allHits.map((hit, i) => (
|
{allHits.map((hit, i) => (
|
||||||
<li
|
<li
|
||||||
className={
|
className={
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import Magnifier from '../../../assets/icons/Magnifier';
|
||||||
|
import { searchPageUrl } from '../../../utils/algolia-locale-setup';
|
||||||
|
|
||||||
|
const SearchBarOptimized = (): JSX.Element => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const placeholder = t('search.placeholder');
|
||||||
|
const searchUrl: string = searchPageUrl as string;
|
||||||
|
const [value, setValue] = useState('');
|
||||||
|
const onChange = (event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setValue(event.target.value);
|
||||||
|
const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (value && value.length > 1) {
|
||||||
|
window.open(`${searchUrl}?query=${encodeURIComponent(value)}`, '_blank');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='fcc_searchBar' data-testid='fcc_searchBar'>
|
||||||
|
<div className='fcc_search_wrapper'>
|
||||||
|
<div className='ais-SearchBox' data-cy='ais-SearchBox'>
|
||||||
|
<form
|
||||||
|
action=''
|
||||||
|
className='ais-SearchBox-form'
|
||||||
|
data-cy='ais-SearchBox-form'
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
role='search'
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-label='Search'
|
||||||
|
autoCapitalize='off'
|
||||||
|
autoComplete='off'
|
||||||
|
autoCorrect='off'
|
||||||
|
className='ais-SearchBox-input'
|
||||||
|
maxLength={512}
|
||||||
|
onChange={onChange}
|
||||||
|
placeholder={placeholder}
|
||||||
|
spellCheck='false'
|
||||||
|
type='search'
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className='ais-SearchBox-submit'
|
||||||
|
title='Submit your search query.'
|
||||||
|
type='submit'
|
||||||
|
>
|
||||||
|
<Magnifier />
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
SearchBarOptimized.displayName = 'SearchBarOptimized';
|
||||||
|
export default SearchBarOptimized;
|
@ -1,56 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
Highlight,
|
|
||||||
connectStateResults,
|
|
||||||
connectAutoComplete
|
|
||||||
} from 'react-instantsearch-dom';
|
|
||||||
import { isEmpty } from 'lodash-es';
|
|
||||||
|
|
||||||
import EmptySearch from './EmptySearch';
|
|
||||||
import NoResults from './NoResults';
|
|
||||||
|
|
||||||
import './search-page-hits.css';
|
|
||||||
|
|
||||||
const AllHits = connectAutoComplete(({ hits, currentRefinement }) => {
|
|
||||||
const isHitsEmpty = !hits.length;
|
|
||||||
|
|
||||||
return currentRefinement && !isHitsEmpty ? (
|
|
||||||
<div className='ais-Hits search-page'>
|
|
||||||
<ul className='ais-Hits-list'>
|
|
||||||
{hits.map(result => (
|
|
||||||
<a
|
|
||||||
href={result.url}
|
|
||||||
key={result.objectID}
|
|
||||||
rel='noopener noreferrer'
|
|
||||||
target='_blank'
|
|
||||||
>
|
|
||||||
<li className='ais-Hits-item dataset-node'>
|
|
||||||
<p>
|
|
||||||
<Highlight attribute='title' hit={result} />
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</a>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<NoResults query={currentRefinement} />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
AllHits.displayName = 'AllHits';
|
|
||||||
|
|
||||||
const SearchHits = connectStateResults(({ handleClick, searchState }) => {
|
|
||||||
const isSearchEmpty = isEmpty(searchState) || isEmpty(searchState.query);
|
|
||||||
|
|
||||||
return isSearchEmpty ? (
|
|
||||||
<EmptySearch />
|
|
||||||
) : (
|
|
||||||
<AllHits handleClick={handleClick} />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
SearchHits.displayName = 'SearchHits';
|
|
||||||
|
|
||||||
export default SearchHits;
|
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
import TestRenderer from 'react-test-renderer';
|
import TestRenderer from 'react-test-renderer';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import toLearnPath from '../utils/to-learn-path';
|
import toLearnPath from '../utils/to-learn-path';
|
||||||
import { withPrefix } from 'gatsby';
|
import { withPrefix } from 'gatsby';
|
||||||
|
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
import React, { Fragment, Component } from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Helmet from 'react-helmet';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Index } from 'react-instantsearch-dom';
|
|
||||||
import { Grid, Row, Col } from '@freecodecamp/react-bootstrap';
|
|
||||||
import { withTranslation } from 'react-i18next';
|
|
||||||
|
|
||||||
import { updateSearchQuery } from '../components/search/redux';
|
|
||||||
import SearchPageHits from '../components/search/searchPage/SearchPageHits';
|
|
||||||
|
|
||||||
import './search.css';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
t: PropTypes.func.isRequired,
|
|
||||||
updateSearchQuery: PropTypes.func.isRequired
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = { updateSearchQuery };
|
|
||||||
|
|
||||||
class SearchPage extends Component {
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.props.updateSearchQuery('');
|
|
||||||
}
|
|
||||||
render() {
|
|
||||||
const { t } = this.props;
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<Helmet title={`${t('search.label')} | freeCodeCamp.org`} />
|
|
||||||
<Index indexName='news' />
|
|
||||||
<Grid>
|
|
||||||
<Row>
|
|
||||||
<Col xs={12}>
|
|
||||||
<main>
|
|
||||||
<SearchPageHits />
|
|
||||||
</main>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Grid>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchPage.displayName = 'SearchPage';
|
|
||||||
SearchPage.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(withTranslation()(SearchPage));
|
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
|
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { ActionsObservable, StateObservable } from 'redux-observable';
|
import { ActionsObservable, StateObservable } from 'redux-observable';
|
||||||
import failedUpdatesEpic from './failed-updates-epic';
|
import failedUpdatesEpic from './failed-updates-epic';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global jest */
|
|
||||||
|
|
||||||
import { types } from '.';
|
import { types } from '.';
|
||||||
import { createGaSaga } from './ga-saga';
|
import { createGaSaga } from './ga-saga';
|
||||||
import ga from '../analytics';
|
import ga from '../analytics';
|
||||||
|
@ -84,69 +84,79 @@ class DesktopLayout extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{projectBasedChallenge && (
|
<ReflexContainer className='desktop-layout' orientation='horizontal'>
|
||||||
<ActionRow switchDisplayTab={this.switchDisplayTab} {...this.state} />
|
{projectBasedChallenge && (
|
||||||
)}
|
<ActionRow
|
||||||
<ReflexContainer className='desktop-layout' orientation='vertical'>
|
switchDisplayTab={this.switchDisplayTab}
|
||||||
{!projectBasedChallenge && (
|
{...this.state}
|
||||||
<ReflexElement
|
/>
|
||||||
flex={instructionPane.flex}
|
|
||||||
name='instructionPane'
|
|
||||||
{...resizeProps}
|
|
||||||
>
|
|
||||||
{instructions}
|
|
||||||
</ReflexElement>
|
|
||||||
)}
|
)}
|
||||||
{!projectBasedChallenge && (
|
<ReflexElement flex={8} {...reflexProps} {...resizeProps}>
|
||||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
<ReflexContainer orientation='vertical'>
|
||||||
)}
|
{!projectBasedChallenge && (
|
||||||
|
|
||||||
<ReflexElement
|
|
||||||
flex={editorPane.flex}
|
|
||||||
name='editorPane'
|
|
||||||
{...resizeProps}
|
|
||||||
>
|
|
||||||
{challengeFile && showUpcomingChanges && !hasEditableBoundries && (
|
|
||||||
<EditorTabs />
|
|
||||||
)}
|
|
||||||
{challengeFile && (
|
|
||||||
<ReflexContainer key={challengeFile.key} orientation='horizontal'>
|
|
||||||
<ReflexElement
|
<ReflexElement
|
||||||
flex={codePane.flex}
|
flex={instructionPane.flex}
|
||||||
name='codePane'
|
name='instructionPane'
|
||||||
{...reflexProps}
|
|
||||||
{...resizeProps}
|
{...resizeProps}
|
||||||
>
|
>
|
||||||
{<Fragment>{editor}</Fragment>}
|
{instructions}
|
||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
{isConsoleDisplayable && (
|
)}
|
||||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
{!projectBasedChallenge && (
|
||||||
)}
|
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||||
{isConsoleDisplayable && (
|
)}
|
||||||
<ReflexElement
|
|
||||||
flex={testsPane.flex}
|
<ReflexElement
|
||||||
name='testsPane'
|
flex={editorPane.flex}
|
||||||
{...reflexProps}
|
name='editorPane'
|
||||||
{...resizeProps}
|
{...resizeProps}
|
||||||
|
>
|
||||||
|
{challengeFile &&
|
||||||
|
showUpcomingChanges &&
|
||||||
|
!hasEditableBoundries && <EditorTabs />}
|
||||||
|
{challengeFile && (
|
||||||
|
<ReflexContainer
|
||||||
|
key={challengeFile.key}
|
||||||
|
orientation='horizontal'
|
||||||
>
|
>
|
||||||
{testOutput}
|
<ReflexElement
|
||||||
</ReflexElement>
|
flex={codePane.flex}
|
||||||
|
name='codePane'
|
||||||
|
{...reflexProps}
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
|
<Fragment>{editor}</Fragment>
|
||||||
|
</ReflexElement>
|
||||||
|
{isConsoleDisplayable && (
|
||||||
|
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||||
|
)}
|
||||||
|
{isConsoleDisplayable && (
|
||||||
|
<ReflexElement
|
||||||
|
flex={testsPane.flex}
|
||||||
|
name='testsPane'
|
||||||
|
{...reflexProps}
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
|
{testOutput}
|
||||||
|
</ReflexElement>
|
||||||
|
)}
|
||||||
|
</ReflexContainer>
|
||||||
)}
|
)}
|
||||||
</ReflexContainer>
|
</ReflexElement>
|
||||||
)}
|
{isPreviewDisplayable && (
|
||||||
|
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||||
|
)}
|
||||||
|
{isPreviewDisplayable && (
|
||||||
|
<ReflexElement
|
||||||
|
flex={previewPane.flex}
|
||||||
|
name='previewPane'
|
||||||
|
{...resizeProps}
|
||||||
|
>
|
||||||
|
{preview}
|
||||||
|
</ReflexElement>
|
||||||
|
)}
|
||||||
|
</ReflexContainer>
|
||||||
</ReflexElement>
|
</ReflexElement>
|
||||||
{isPreviewDisplayable && (
|
|
||||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
|
||||||
)}
|
|
||||||
{isPreviewDisplayable && (
|
|
||||||
<ReflexElement
|
|
||||||
flex={previewPane.flex}
|
|
||||||
name='previewPane'
|
|
||||||
{...resizeProps}
|
|
||||||
>
|
|
||||||
{preview}
|
|
||||||
</ReflexElement>
|
|
||||||
)}
|
|
||||||
</ReflexContainer>
|
</ReflexContainer>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import renderer from 'react-test-renderer';
|
import renderer from 'react-test-renderer';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
|
|
||||||
import { getCompletedPercent } from './CompletionModal';
|
import { getCompletedPercent } from './CompletionModal';
|
||||||
|
|
||||||
jest.mock('../../../analytics');
|
jest.mock('../../../analytics');
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global jest, expect */
|
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render, fireEvent } from '@testing-library/react';
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
import { findIndexHtml } from './builders.js';
|
import { findIndexHtml } from './builders.js';
|
||||||
|
|
||||||
const withHTML = [
|
const withHTML = [
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import { transformEditorLink } from '../utils';
|
import { transformEditorLink } from '../utils';
|
||||||
|
|
||||||
describe('create-question-epic', () => {
|
describe('create-question-epic', () => {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import envData from '../../../../../config/env.json';
|
import envData from '../../../../../config/env.json';
|
||||||
|
|
||||||
const { forumLocation } = envData;
|
const { forumLocation } = envData;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect, jest */
|
|
||||||
|
|
||||||
import createWorker from './worker-executor';
|
import createWorker from './worker-executor';
|
||||||
|
|
||||||
function mockWorker({ init, postMessage, terminate } = {}) {
|
function mockWorker({ init, postMessage, terminate } = {}) {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global describe it expect */
|
|
||||||
import { cssString } from './__fixtures/curriculum-helpers-css';
|
import { cssString } from './__fixtures/curriculum-helpers-css';
|
||||||
import CSSHelp from './css-help';
|
import CSSHelp from './css-help';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global describe it expect */
|
|
||||||
|
|
||||||
import __testHelpers, { removeJSComments } from './curriculum-helpers';
|
import __testHelpers, { removeJSComments } from './curriculum-helpers';
|
||||||
import jsTestValues from './__fixtures/curriculum-helpers-javascript';
|
import jsTestValues from './__fixtures/curriculum-helpers-javascript';
|
||||||
import cssTestValues from './__fixtures/curriculum-helpers-css';
|
import cssTestValues from './__fixtures/curriculum-helpers-css';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* global expect BigInt */
|
/* global BigInt */
|
||||||
|
|
||||||
const { format } = require('./format');
|
const { format } = require('./format');
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
import { isObject } from 'lodash-es';
|
import { isObject } from 'lodash-es';
|
||||||
import {
|
import {
|
||||||
isHandledError,
|
isHandledError,
|
||||||
|
66
client/src/utils/path-parsers.test.js
Normal file
66
client/src/utils/path-parsers.test.js
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* global describe it expect */
|
||||||
|
|
||||||
|
import { isChallenge, isLanding } from './path-parsers';
|
||||||
|
|
||||||
|
const pathnames = {
|
||||||
|
english: {
|
||||||
|
landing: '/',
|
||||||
|
superBlock: '/learn/responsive-web-design',
|
||||||
|
challenge:
|
||||||
|
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements'
|
||||||
|
},
|
||||||
|
espanol: {
|
||||||
|
landing: '/espanol',
|
||||||
|
superBlock: '/espanol/learn/responsive-web-design',
|
||||||
|
challenge:
|
||||||
|
'/espanol/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('isLanding', () => {
|
||||||
|
it('returns a booleans', () => {
|
||||||
|
expect(typeof isLanding('/')).toBe('boolean');
|
||||||
|
});
|
||||||
|
it('return true for Espanol landing pathname', () => {
|
||||||
|
expect(isLanding(pathnames.espanol.landing)).toBe(true);
|
||||||
|
});
|
||||||
|
it('returns false for Espanol super block pathname', () => {
|
||||||
|
expect(isLanding(pathnames.espanol.superBlock)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns false for Espanol challenge pathname', () => {
|
||||||
|
expect(isLanding(pathnames.espanol.challenge)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns true for English landing pathname', () => {
|
||||||
|
expect(isLanding(pathnames.english.landing)).toBe(true);
|
||||||
|
});
|
||||||
|
it('returns false for English super block pathname', () => {
|
||||||
|
expect(isLanding(pathnames.english.superBlock)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns false for English challenge pathname', () => {
|
||||||
|
expect(isLanding(pathnames.english.challenge)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isChallenge', () => {
|
||||||
|
it('returns a boolean', () => {
|
||||||
|
expect(typeof isChallenge('/')).toBe('boolean');
|
||||||
|
});
|
||||||
|
it('returns false for Espanol landing pathname', () => {
|
||||||
|
expect(isChallenge(pathnames.espanol.landing)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns false for Espanol super block pathname', () => {
|
||||||
|
expect(isChallenge(pathnames.espanol.superBlock)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns true for Espanol challenge pathname', () => {
|
||||||
|
expect(isChallenge(pathnames.espanol.challenge)).toBe(true);
|
||||||
|
});
|
||||||
|
it('returns false for English landing pathname', () => {
|
||||||
|
expect(isChallenge(pathnames.english.landing)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns false for English super block pathname', () => {
|
||||||
|
expect(isChallenge(pathnames.english.superBlock)).toBe(false);
|
||||||
|
});
|
||||||
|
it('returns true for English challenge pathname', () => {
|
||||||
|
expect(isChallenge(pathnames.english.challenge)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
23
client/src/utils/path-parsers.ts
Normal file
23
client/src/utils/path-parsers.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { i18nConstants } from '../../../config/constants';
|
||||||
|
|
||||||
|
const splitPath = (pathname: string): string[] =>
|
||||||
|
pathname.split('/').filter(x => x);
|
||||||
|
|
||||||
|
export const isChallenge = (pathname: string): boolean => {
|
||||||
|
const pathArray = splitPath(pathname);
|
||||||
|
return (
|
||||||
|
(pathArray.length === 4 && pathArray[0]) === 'learn' ||
|
||||||
|
(pathArray.length === 5 && pathArray[1]) === 'learn'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isLanding = (pathname: string): boolean => {
|
||||||
|
const pathArray = splitPath(pathname);
|
||||||
|
const isEnglishLanding = pathArray.length === 0;
|
||||||
|
const isI18Landing =
|
||||||
|
pathArray.length === 1 && i18nConstants.includes(pathArray[0]);
|
||||||
|
return isEnglishLanding || isI18Landing;
|
||||||
|
};
|
||||||
|
|
||||||
|
const pathParsers = { isLanding, isChallenge };
|
||||||
|
export default pathParsers;
|
@ -1,5 +1,10 @@
|
|||||||
{
|
{
|
||||||
"include": ["./i18n/**/*", "./plugins/**/*", "./src/**/*", "./utils/**/*"],
|
"include": [
|
||||||
|
"./i18n/**/*",
|
||||||
|
"./plugins/**/*",
|
||||||
|
"./src/**/*",
|
||||||
|
"./utils/**/*"
|
||||||
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2020",
|
"target": "es2020",
|
||||||
"module": "es2020",
|
"module": "es2020",
|
||||||
@ -11,7 +16,12 @@
|
|||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"resolveJsonModule": true
|
"types": [
|
||||||
|
"node",
|
||||||
|
"jest",
|
||||||
|
"@testing-library/jest-dom"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ import {
|
|||||||
DefaultLayout
|
DefaultLayout
|
||||||
} from '../../src/components/layouts';
|
} from '../../src/components/layouts';
|
||||||
import FourOhFourPage from '../../src/pages/404';
|
import FourOhFourPage from '../../src/pages/404';
|
||||||
|
import { isChallenge } from '../../src/utils/path-parsers';
|
||||||
|
|
||||||
export default function layoutSelector({ element, props }) {
|
export default function layoutSelector({ element, props }) {
|
||||||
const {
|
const {
|
||||||
@ -18,32 +19,23 @@ export default function layoutSelector({ element, props }) {
|
|||||||
{element}
|
{element}
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
);
|
);
|
||||||
}
|
} else if (/\/certification\//.test(pathname)) {
|
||||||
if (/\/certification\//.test(pathname)) {
|
|
||||||
return (
|
return (
|
||||||
<CertificationLayout pathname={pathname}>{element}</CertificationLayout>
|
<CertificationLayout pathname={pathname}>{element}</CertificationLayout>
|
||||||
);
|
);
|
||||||
}
|
} else if (isChallenge(pathname)) {
|
||||||
|
|
||||||
const splitPath = pathname.split('/').filter(x => x);
|
|
||||||
|
|
||||||
const isChallenge =
|
|
||||||
(splitPath.length === 4 && splitPath[0]) === 'learn' ||
|
|
||||||
(splitPath.length === 5 && splitPath[1]) === 'learn';
|
|
||||||
|
|
||||||
if (isChallenge) {
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout pathname={pathname} showFooter={false}>
|
<DefaultLayout pathname={pathname} showFooter={false}>
|
||||||
{element}
|
{element}
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<DefaultLayout pathname={pathname} showFooter={true}>
|
||||||
|
{element}
|
||||||
|
</DefaultLayout>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<DefaultLayout pathname={pathname} showFooter={true}>
|
|
||||||
{element}
|
|
||||||
</DefaultLayout>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layoutSelector.propTypes = {
|
layoutSelector.propTypes = {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
/* global expect jest */
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
/* global expect */
|
|
||||||
|
|
||||||
import { injectConditionalTags } from './tags';
|
import { injectConditionalTags } from './tags';
|
||||||
|
|
||||||
describe('Tags', () => {
|
describe('Tags', () => {
|
||||||
|
@ -4,8 +4,44 @@ for (let i = 0; i < 26; i++) {
|
|||||||
alphabet = alphabet.concat(String.fromCharCode(97 + i));
|
alphabet = alphabet.concat(String.fromCharCode(97 + i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const i18nConstants = [
|
||||||
|
// reserved paths for localizations
|
||||||
|
'afrikaans',
|
||||||
|
'arabic',
|
||||||
|
'bengali',
|
||||||
|
'catalan',
|
||||||
|
'chinese',
|
||||||
|
'czech',
|
||||||
|
'danish',
|
||||||
|
'dutch',
|
||||||
|
'espanol',
|
||||||
|
'finnish',
|
||||||
|
'french',
|
||||||
|
'german',
|
||||||
|
'greek',
|
||||||
|
'hebrew',
|
||||||
|
'hindi',
|
||||||
|
'hungarian',
|
||||||
|
'italian',
|
||||||
|
'japanese',
|
||||||
|
'korean',
|
||||||
|
'norwegian',
|
||||||
|
'polish',
|
||||||
|
'portuguese',
|
||||||
|
'romanian',
|
||||||
|
'russian',
|
||||||
|
'serbian',
|
||||||
|
'spanish',
|
||||||
|
'swahili',
|
||||||
|
'swedish',
|
||||||
|
'turkish',
|
||||||
|
'ukrainian',
|
||||||
|
'vietnamese'
|
||||||
|
];
|
||||||
|
|
||||||
let blocklist = [
|
let blocklist = [
|
||||||
...alphabet.split(''),
|
...alphabet.split(''),
|
||||||
|
...i18nConstants,
|
||||||
'about',
|
'about',
|
||||||
'academic-honesty',
|
'academic-honesty',
|
||||||
'account',
|
'account',
|
||||||
@ -85,40 +121,6 @@ let blocklist = [
|
|||||||
'user',
|
'user',
|
||||||
'username',
|
'username',
|
||||||
'wiki',
|
'wiki',
|
||||||
|
|
||||||
// reserved paths for localizations
|
|
||||||
'afrikaans',
|
|
||||||
'arabic',
|
|
||||||
'bengali',
|
|
||||||
'catalan',
|
|
||||||
'chinese',
|
|
||||||
'czech',
|
|
||||||
'danish',
|
|
||||||
'dutch',
|
|
||||||
'espanol',
|
|
||||||
'finnish',
|
|
||||||
'french',
|
|
||||||
'german',
|
|
||||||
'greek',
|
|
||||||
'hebrew',
|
|
||||||
'hindi',
|
|
||||||
'hungarian',
|
|
||||||
'italian',
|
|
||||||
'japanese',
|
|
||||||
'korean',
|
|
||||||
'norwegian',
|
|
||||||
'polish',
|
|
||||||
'portuguese',
|
|
||||||
'romanian',
|
|
||||||
'russian',
|
|
||||||
'serbian',
|
|
||||||
'spanish',
|
|
||||||
'swahili',
|
|
||||||
'swedish',
|
|
||||||
'turkish',
|
|
||||||
'ukrainian',
|
|
||||||
'vietnamese',
|
|
||||||
|
|
||||||
// some more names from https://github.com/marteinn/The-Big-Username-Blacklist-JS/blob/master/src/list.js
|
// some more names from https://github.com/marteinn/The-Big-Username-Blacklist-JS/blob/master/src/list.js
|
||||||
'.htaccess',
|
'.htaccess',
|
||||||
'.htpasswd',
|
'.htpasswd',
|
||||||
@ -647,4 +649,5 @@ let blocklist = [
|
|||||||
'zlib'
|
'zlib'
|
||||||
];
|
];
|
||||||
|
|
||||||
export const blocklistedUsernames = [...new Set(blocklist)];
|
exports.blocklistedUsernames = [...new Set(blocklist)];
|
||||||
|
exports.i18nConstants = i18nConstants;
|
@ -42,7 +42,7 @@ dashedName: nest-an-anchor-element-within-a-paragraph
|
|||||||
|
|
||||||
此示例的最終輸出結果是這樣:
|
此示例的最終輸出結果是這樣:
|
||||||
|
|
||||||
你可以訪問 <a href="https://www.freecodecamp.org" target="_blank">link to www.freecodecamp.org</a>。
|
Here's a <a href="https://www.freecodecamp.org" target="_blank">link to www.freecodecamp.org</a> for you to follow.
|
||||||
|
|
||||||
# --instructions--
|
# --instructions--
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Animal.prototype.eat = function() {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
在這一節以及下一節挑戰中我們將學習如何在 `Bird` 和 `Dog` 中重用 `Animal's` 中的方法,而無需重新定義它們。 這裏我們會用到構造函數的繼承特性。 這一節挑戰中我們學習第一步:創建一個超類 `supertype`(或者叫父類)的實例。 你已經學會了一種創建 `Animal` 實例的方法,即使用 `new` 操作符:
|
在這一節以及下一節挑戰中我們將學習如何在 `Bird` 和 `Dog` 中重用 `Animal` 中的方法,而無需重新定義它們。 這裏我們會用到構造函數的繼承特性。 這一節挑戰中我們學習第一步:創建一個超類 `supertype`(或者叫父類)的實例。 你已經學會了一種創建 `Animal` 實例的方法,即使用 `new` 操作符:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let animal = new Animal();
|
let animal = new Animal();
|
||||||
@ -29,7 +29,7 @@ let animal = new Animal();
|
|||||||
let animal = Object.create(Animal.prototype);
|
let animal = Object.create(Animal.prototype);
|
||||||
```
|
```
|
||||||
|
|
||||||
`Object.create(obj)` 創建了一個新對象,並指定了 `obj` 作爲新對象的 `prototype`。 回憶一下,我們之前說過 `prototype` 就像是創建對象的“配方”。 如果我們把 `animal` 的 `prototype` 設置爲與 `Animal's` 構造函數的 `prototype` 一樣,那麼就相當於讓 `animal` 這個實例的配方與 `Animal` 其他實例的配方一樣了。
|
`Object.create(obj)` 創建了一個新對象,並指定了 `obj` 作爲新對象的 `prototype`。 回憶一下,我們之前說過 `prototype` 就像是創建對象的“配方”。 如果我們把 `animal` 的 `prototype` 設置爲與 `Animal` 構造函數的 `prototype` 一樣,那麼就相當於讓 `animal` 這個實例具有與 `Animal` 的其他實例相同的“配方”了。
|
||||||
|
|
||||||
```js
|
```js
|
||||||
animal.eat();
|
animal.eat();
|
||||||
|
@ -36,7 +36,7 @@ Bird.prototype.eat = function() {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
如果你有一個實例:`let duck = new Bird();`,然後你調用了 `duck.eat()`,以下就是 JavaScript 在 `duck’s` 的 `prototype` 鏈上尋找方法的過程:
|
如果你有一個實例:`let duck = new Bird();`,然後你調用了 `duck.eat()`,以下就是 JavaScript 在 `duck` 的 `prototype` 鏈上尋找方法的過程:
|
||||||
|
|
||||||
1. `duck` => `eat()` 是定義在這裏嗎? 不是。
|
1. `duck` => `eat()` 是定義在這裏嗎? 不是。
|
||||||
2. `Bird` => `eat()` 是定義在這裏嗎? => 是的。 執行它並停止往上搜索。
|
2. `Bird` => `eat()` 是定義在這裏嗎? => 是的。 執行它並停止往上搜索。
|
||||||
|
@ -19,7 +19,7 @@ let duck = new Bird();
|
|||||||
duck.constructor
|
duck.constructor
|
||||||
```
|
```
|
||||||
|
|
||||||
但是 `duck` 和其他所有 `Bird` 的實例都應該表明它們是由 `Bird` 創建的,而不是由 `Animal` 創建的。 爲此,你可以手動把 `Bird's` 的 constructor 屬性設置爲 `Bird` 對象:
|
但是 `duck` 和其他所有 `Bird` 的實例都應該表明它們是由 `Bird` 創建的,而不是由 `Animal` 創建的。 爲此,你可以手動將 `Bird` 的構造函數屬性設置爲 `Bird` 對象:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
Bird.prototype.constructor = Bird;
|
Bird.prototype.constructor = Bird;
|
||||||
|
@ -12,7 +12,7 @@ dashedName: use-prototype-properties-to-reduce-duplicate-code
|
|||||||
|
|
||||||
當只有兩個實例時可能並不是什麼問題,但想象一下如果有數百萬個實例。 這將會產生許許多多重複的變量。
|
當只有兩個實例時可能並不是什麼問題,但想象一下如果有數百萬個實例。 這將會產生許許多多重複的變量。
|
||||||
|
|
||||||
這裏有一個更好的方法可以解決上述問題,那就是使用 `Bird’s` 的 `prototype`。 `prototype` 是一個可以在所有 `Bird` 實例之間共享的對象。 以下是一個在 `Bird prototype` 中添加 `numLegs` 屬性的示例:
|
更好的方法是使用 `Bird` 的 `prototype`。 `prototype` 是一個可以在所有 `Bird` 實例之間共享的對象。 以下是一個在 `Bird prototype` 中添加 `numLegs` 屬性的示例:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
Bird.prototype.numLegs = 2;
|
Bird.prototype.numLegs = 2;
|
||||||
@ -45,7 +45,7 @@ assert(beagle.numLegs !== undefined);
|
|||||||
assert(typeof beagle.numLegs === 'number');
|
assert(typeof beagle.numLegs === 'number');
|
||||||
```
|
```
|
||||||
|
|
||||||
`numLegs` 應該是一個 `prototype` 屬性而不是一個 `own` 屬性。
|
`numLegs` 應該是一個 `prototype` 屬性,而不是一個自身屬性。
|
||||||
|
|
||||||
```js
|
```js
|
||||||
assert(beagle.hasOwnProperty('numLegs') === false);
|
assert(beagle.hasOwnProperty('numLegs') === false);
|
||||||
|
@ -16,7 +16,7 @@ dashedName: create-a-controlled-form
|
|||||||
|
|
||||||
我們增加了一個提交表單的按鈕。 可以看到它的 `type` 被設置爲 `submit`,表明它是控制表單提交的按鈕。 在 `form` 中添加 `input` 元素,並像上個挑戰一樣設置其 `value` 和 `onChange()` 屬性。 然後,應該完成 `handleSubmit` 方法,以便將組件 state 屬性 `submit` 設置爲本地 `state` 下的當前輸入值。
|
我們增加了一個提交表單的按鈕。 可以看到它的 `type` 被設置爲 `submit`,表明它是控制表單提交的按鈕。 在 `form` 中添加 `input` 元素,並像上個挑戰一樣設置其 `value` 和 `onChange()` 屬性。 然後,應該完成 `handleSubmit` 方法,以便將組件 state 屬性 `submit` 設置爲本地 `state` 下的當前輸入值。
|
||||||
|
|
||||||
**注意:** 還必須在提交處理程序中調用 `event.preventDefault()`,以防止默認的表單提交行爲刷新網頁。
|
**注意:** 還必須在提交處理程序中調用 `event.preventDefault()`,以防止將會刷新網頁的默認的表單提交行爲。 爲了便於學員操作,默認行爲在這裏被禁用,以防止重置挑戰的代碼。
|
||||||
|
|
||||||
最後,在 `form` 元素之後創建一個 `h1` 標籤,該標籤從組件的 `state` 渲染 `submit` 的值。 然後,可以在表單中鍵入任何內容,然後單擊按鈕(或按 enter 鍵),輸入會渲染到頁面上。
|
最後,在 `form` 元素之後創建一個 `h1` 標籤,該標籤從組件的 `state` 渲染 `submit` 的值。 然後,可以在表單中鍵入任何內容,然後單擊按鈕(或按 enter 鍵),輸入會渲染到頁面上。
|
||||||
|
|
||||||
@ -98,7 +98,26 @@ assert(
|
|||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
`h1` 標題應該從組件的 state 渲染 `submit` 字段的值。
|
`handleSubmit` 應該調用 `event.preventDefault`。
|
||||||
|
|
||||||
|
```js
|
||||||
|
const handleSubmit = MyForm.prototype.handleSubmit.toString();
|
||||||
|
const allMatches = handleSubmit.match(/\bevent\.preventDefault\(\s*?\)/g) ?? [];
|
||||||
|
const blockCommented = handleSubmit.match(
|
||||||
|
/\/\*.*?\bevent\.preventDefault\(\s*?\).*?\*\//gs
|
||||||
|
);
|
||||||
|
const lineCommented = handleSubmit.match(
|
||||||
|
/\/\/.*?\bevent\.preventDefault\(\s*?\)/g
|
||||||
|
);
|
||||||
|
const commentedMatches = [...(blockCommented ?? []), ...(lineCommented ?? [])];
|
||||||
|
|
||||||
|
assert(
|
||||||
|
// At least one event.preventDefault() call exists and is not commented out
|
||||||
|
allMatches.length > commentedMatches.length
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`h1` 標頭應該從組件的 state 渲染 `submit` 字段的值。
|
||||||
|
|
||||||
```js
|
```js
|
||||||
(() => {
|
(() => {
|
||||||
|
@ -18,8 +18,7 @@ dashedName: personal-library
|
|||||||
|
|
||||||
# --instructions--
|
# --instructions--
|
||||||
|
|
||||||
1. 將的 MongoDB 連接字符串添加到 `.env` 中(沒有引號),`DB`
|
1. 將你的 MongoDB 連接字符串添加到 `.env` 中,作爲 `DB` 示例:`DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
|
||||||
示例: `DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
|
|
||||||
2. 在 `.env` 文件中設置 `NODE_ENV` 爲 `test`中,沒有引號
|
2. 在 `.env` 文件中設置 `NODE_ENV` 爲 `test`中,沒有引號
|
||||||
3. 需要在 `routes/api.js` 中創建所有路由
|
3. 需要在 `routes/api.js` 中創建所有路由
|
||||||
4. 在 `tests/2_functional-tests.js` 中創建所有的功能測試
|
4. 在 `tests/2_functional-tests.js` 中創建所有的功能測試
|
||||||
@ -66,7 +65,7 @@ async (getUserInput) => {
|
|||||||
let a = $.post(url, { title: 'Faux Book A' });
|
let a = $.post(url, { title: 'Faux Book A' });
|
||||||
let b = $.post(url, { title: 'Faux Book B' });
|
let b = $.post(url, { title: 'Faux Book B' });
|
||||||
let c = $.post(url, { title: 'Faux Book C' });
|
let c = $.post(url, { title: 'Faux Book C' });
|
||||||
Promise.all([a, b, c]).then(async () => {
|
await Promise.all([a, b, c]).then(async () => {
|
||||||
let data = await $.get(url);
|
let data = await $.get(url);
|
||||||
assert.isArray(data);
|
assert.isArray(data);
|
||||||
assert.isAtLeast(data.length, 3);
|
assert.isAtLeast(data.length, 3);
|
||||||
@ -214,8 +213,8 @@ async (getUserInput) => {
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
/**
|
/**
|
||||||
Backend challenges don't need solutions,
|
Backend challenges don't need solutions,
|
||||||
because they would need to be tested against a full working project.
|
because they would need to be tested against a full working project.
|
||||||
Please check our contributing guidelines to learn more.
|
Please check our contributing guidelines to learn more.
|
||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f060b6c005b0e76f05b
|
id: 5e7b9f060b6c005b0e76f05b
|
||||||
title: Build your own Functions
|
title: 編寫你自己的函數
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: nLDychdBwUg
|
videoId: nLDychdBwUg
|
||||||
dashedName: build-your-own-functions
|
dashedName: build-your-own-functions
|
||||||
@ -8,15 +8,15 @@ dashedName: build-your-own-functions
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源
|
||||||
|
|
||||||
\- [Exercise](https://www.youtube.com/watch?v=ksvGhDsjtpw)
|
\- [練習](https://www.youtube.com/watch?v=ksvGhDsjtpw)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What will the following Python program print out?:
|
下面這個 Python 程序會打印什麼?:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def fred():
|
def fred():
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f0b0b6c005b0e76f06d
|
id: 5e7b9f0b0b6c005b0e76f06d
|
||||||
title: Comparing and Sorting Tuples
|
title: Tuples 的比較和排序
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: dZXzBXUxxCs
|
videoId: dZXzBXUxxCs
|
||||||
dashedName: comparing-and-sorting-tuples
|
dashedName: comparing-and-sorting-tuples
|
||||||
@ -8,15 +8,15 @@ dashedName: comparing-and-sorting-tuples
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源:
|
||||||
|
|
||||||
\- [Exercise](https://www.youtube.com/watch?v=EhQxwzyT16E)
|
\- [練習](https://www.youtube.com/watch?v=EhQxwzyT16E)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Which does the same thing as the following code?:
|
哪個代碼與示例代碼完成相同的功能?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
lst = []
|
lst = []
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f050b6c005b0e76f058
|
id: 5e7b9f050b6c005b0e76f058
|
||||||
title: Conditional Execution
|
title: 根據條件執行
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: gz_IfIsZQtc
|
videoId: gz_IfIsZQtc
|
||||||
dashedName: conditional-execution
|
dashedName: conditional-execution
|
||||||
@ -10,7 +10,7 @@ dashedName: conditional-execution
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Which code is indented correctly to print "Yes" if x = 0 and y = 10?
|
以下哪個代碼縮進,能夠滿足當“x=0”和“y=10”的時,打印 "Yes"?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f6a0b6c005b0e76f097
|
id: 5e7b9f6a0b6c005b0e76f097
|
||||||
title: 'Data Visualization: Mailing Lists'
|
title: '數據可視化:郵件列表'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: RYdW660KkaQ
|
videoId: RYdW660KkaQ
|
||||||
dashedName: data-visualization-mailing-lists
|
dashedName: data-visualization-mailing-lists
|
||||||
@ -8,27 +8,27 @@ dashedName: data-visualization-mailing-lists
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源:
|
||||||
|
|
||||||
\- [Exercise: Geodata](https://www.youtube.com/watch?v=KfhslNzopxo)
|
\- [練習:Geodata](https://www.youtube.com/watch?v=KfhslNzopxo)
|
||||||
|
|
||||||
\- [Exercise: Gmane Model](https://www.youtube.com/watch?v=wSpl1-7afAk)
|
\- [練習:Gmane 模型](https://www.youtube.com/watch?v=wSpl1-7afAk)
|
||||||
|
|
||||||
\- [Exercise: Gmane Spider](https://www.youtube.com/watch?v=H3w4lOFBUOI)
|
\- [練習:Gmane Spider](https://www.youtube.com/watch?v=H3w4lOFBUOI)
|
||||||
|
|
||||||
\- [Exercise: Gmane Viz](https://www.youtube.com/watch?v=LRqVPMEXByw)
|
\- [練習:Gmane Viz](https://www.youtube.com/watch?v=LRqVPMEXByw)
|
||||||
|
|
||||||
\- [Exercise: Page Rank](https://www.youtube.com/watch?v=yFRAZBkBDBs)
|
\- [練習:Page Rank](https://www.youtube.com/watch?v=yFRAZBkBDBs)
|
||||||
|
|
||||||
\- [Exercise: Page Spider](https://www.youtube.com/watch?v=sXedPQ_AnWA)
|
\- [練習:Page Spider](https://www.youtube.com/watch?v=sXedPQ_AnWA)
|
||||||
|
|
||||||
\- [Exercise: Page Viz](https://www.youtube.com/watch?v=Fm0hpkxsZoo)
|
\- [練習: Page Viz](https://www.youtube.com/watch?v=Fm0hpkxsZoo)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Which is a common JavaScript visualization library?
|
哪個是常見的 JavaScript 可視化庫?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f6a0b6c005b0e76f096
|
id: 5e7b9f6a0b6c005b0e76f096
|
||||||
title: 'Data Visualization: Page Rank'
|
title: '數據可視化:頁面排名'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: 6-w_qIUwaxU
|
videoId: 6-w_qIUwaxU
|
||||||
dashedName: data-visualization-page-rank
|
dashedName: data-visualization-page-rank
|
||||||
@ -10,19 +10,19 @@ dashedName: data-visualization-page-rank
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
How does the PageRank algorithm work?
|
頁面排名算法是如何工作的?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
It determines which pages are most highly connected.
|
它決定哪些頁面之間的連接程度最高。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
It ranks pages based on view counts.
|
它用瀏覽量來決定排名
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
It figures out which pages contain the most important content.
|
它能判斷出哪些頁面包含最重要的內容。
|
||||||
|
|
||||||
## --video-solution--
|
## --video-solution--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f0a0b6c005b0e76f069
|
id: 5e7b9f0a0b6c005b0e76f069
|
||||||
title: Dictionaries and Loops
|
title: 字典和循環
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: EEmekKiKG70
|
videoId: EEmekKiKG70
|
||||||
dashedName: dictionaries-and-loops
|
dashedName: dictionaries-and-loops
|
||||||
@ -8,15 +8,15 @@ dashedName: dictionaries-and-loops
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源:
|
||||||
|
|
||||||
\- [Exercise](https://www.youtube.com/watch?v=PrhZ9qwBDD8)
|
\- [練習](https://www.youtube.com/watch?v=PrhZ9qwBDD8)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What will the following code print?:
|
以下代碼將打印什麼?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
|
counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
|
||||||
@ -42,7 +42,7 @@ jan 100</pre>
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<pre>[Error]</pre>
|
<pre>[Error](錯誤)</pre>
|
||||||
|
|
||||||
## --video-solution--
|
## --video-solution--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f070b6c005b0e76f061
|
id: 5e7b9f070b6c005b0e76f061
|
||||||
title: Intermediate Strings
|
title: 中間字符串
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: KgT_fYLXnyk
|
videoId: KgT_fYLXnyk
|
||||||
dashedName: intermediate-strings
|
dashedName: intermediate-strings
|
||||||
@ -8,15 +8,15 @@ dashedName: intermediate-strings
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資料:
|
||||||
|
|
||||||
\- [Exercise](https://www.youtube.com/watch?v=1bSqHot-KwE)
|
[練習](https://www.youtube.com/watch?v=1bSqHot-KwE)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What is the value of i in the following code?
|
下面的代碼中 i 的值是什麼?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
word = "bananana"
|
word = "bananana"
|
||||||
@ -37,7 +37,7 @@ nanana
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
True
|
True(真)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e6a54c358d3af90110a60a3
|
id: 5e6a54c358d3af90110a60a3
|
||||||
title: 'Introduction: Elements of Python'
|
title: '導言:Python 的元素'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: aRY_xjL35v0
|
videoId: aRY_xjL35v0
|
||||||
dashedName: introduction-elements-of-python
|
dashedName: introduction-elements-of-python
|
||||||
@ -10,7 +10,7 @@ dashedName: introduction-elements-of-python
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What will the following program print out:
|
以下代碼將打印出什麼?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
x = 43
|
x = 43
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e6a54af58d3af90110a60a1
|
id: 5e6a54af58d3af90110a60a1
|
||||||
title: 'Introduction: Hardware Architecture'
|
title: '導言:硬件架構'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: H6qtjRTfSog
|
videoId: H6qtjRTfSog
|
||||||
dashedName: introduction-hardware-architecture
|
dashedName: introduction-hardware-architecture
|
||||||
@ -10,19 +10,19 @@ dashedName: introduction-hardware-architecture
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Where are your programs stored when they are running?
|
當你的程序運作時,它被存儲在哪裏?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
Hard Drive.
|
硬盤。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Memory.
|
內存。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Central Processing Unit.
|
中央處理器。
|
||||||
|
|
||||||
## --video-solution--
|
## --video-solution--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e6a54ba58d3af90110a60a2
|
id: 5e6a54ba58d3af90110a60a2
|
||||||
title: 'Introduction: Python as a Language'
|
title: '導言:Python 這門編程語言'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: 0QeGbZNS_bY
|
videoId: 0QeGbZNS_bY
|
||||||
dashedName: introduction-python-as-a-language
|
dashedName: introduction-python-as-a-language
|
||||||
@ -10,7 +10,7 @@ dashedName: introduction-python-as-a-language
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What will print out after running these two lines of code:
|
運行這兩行代碼後打印出什麼樣的內容:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
x = 6
|
x = 6
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e6a54a558d3af90110a60a0
|
id: 5e6a54a558d3af90110a60a0
|
||||||
title: 'Introduction: Why Program?'
|
title: '導言:爲何編程?'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: 3muQV-Im3Z0
|
videoId: 3muQV-Im3Z0
|
||||||
dashedName: introduction-why-program
|
dashedName: introduction-why-program
|
||||||
@ -8,29 +8,29 @@ dashedName: introduction-why-program
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源:
|
||||||
|
|
||||||
\- [Install Python on Windows](https://youtu.be/F7mtLrYzZP8)
|
\- [Windows 系統安裝 Python](https://youtu.be/F7mtLrYzZP8)
|
||||||
|
|
||||||
\- [Install Python on MacOS](https://youtu.be/wfLnZP-4sZw)
|
\- [MacOS 系統安裝 Python](https://youtu.be/wfLnZP-4sZw)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Who should learn to program?
|
誰應該學習編程?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
College students.
|
大學生。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
People who want to become software developers.
|
想成爲軟件開發者的人。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Everyone.
|
所有人。
|
||||||
|
|
||||||
## --video-solution--
|
## --video-solution--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f070b6c005b0e76f05d
|
id: 5e7b9f070b6c005b0e76f05d
|
||||||
title: 'Iterations: Definite Loops'
|
title: '迭代:定義循環'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: hiRTRAqNlpE
|
videoId: hiRTRAqNlpE
|
||||||
dashedName: iterations-definite-loops
|
dashedName: iterations-definite-loops
|
||||||
@ -10,7 +10,7 @@ dashedName: iterations-definite-loops
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
How many lines will the following code print?:
|
以下代碼將打印多少行?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
for i in [2,1,5]:
|
for i in [2,1,5]:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f070b6c005b0e76f05e
|
id: 5e7b9f070b6c005b0e76f05e
|
||||||
title: 'Iterations: Loop Idioms'
|
title: '迭代:循環成語'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: AelGAcoMXbI
|
videoId: AelGAcoMXbI
|
||||||
dashedName: iterations-loop-idioms
|
dashedName: iterations-loop-idioms
|
||||||
@ -10,7 +10,7 @@ dashedName: iterations-loop-idioms
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Below is code to find the smallest value from a list of values. One line has an error that will cause the code to not work as expected. Which line is it?:
|
以下是一個如何在一串數值中找到最小的數值的代碼。 一行代碼有錯誤,導致整個代碼無法和預期一樣的運行。 那麼是哪一行?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
smallest = None
|
smallest = None
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f070b6c005b0e76f05f
|
id: 5e7b9f070b6c005b0e76f05f
|
||||||
title: 'Iterations: More Patterns'
|
title: '迭代:更多模式'
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: 9Wtqo6vha1M
|
videoId: 9Wtqo6vha1M
|
||||||
dashedName: iterations-more-patterns
|
dashedName: iterations-more-patterns
|
||||||
@ -8,15 +8,15 @@ dashedName: iterations-more-patterns
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源:
|
||||||
|
|
||||||
\- [Exercise](https://www.youtube.com/watch?v=kjxXZQw0uPg)
|
\- [練習](https://www.youtube.com/watch?v=kjxXZQw0uPg)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Which of these evaluates to False?
|
其中哪一個評估爲 False(假)?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f060b6c005b0e76f05c
|
id: 5e7b9f060b6c005b0e76f05c
|
||||||
title: Loops and Iterations
|
title: 循環和迭代
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: dLA-szNRnUY
|
videoId: dLA-szNRnUY
|
||||||
dashedName: loops-and-iterations
|
dashedName: loops-and-iterations
|
||||||
@ -10,7 +10,7 @@ dashedName: loops-and-iterations
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What will the following code print out?:
|
這些代碼會打印出什麼?
|
||||||
|
|
||||||
```python
|
```python
|
||||||
n = 0
|
n = 0
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f170b6c005b0e76f08b
|
id: 5e7b9f170b6c005b0e76f08b
|
||||||
title: Make a Relational Database
|
title: 建立關係數據庫
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: MQ5z4bdF92U
|
videoId: MQ5z4bdF92U
|
||||||
dashedName: make-a-relational-database
|
dashedName: make-a-relational-database
|
||||||
@ -10,7 +10,7 @@ dashedName: make-a-relational-database
|
|||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
What SQL command would you use to retrieve all users that have the email address `quincy@freecodecamp.org`?
|
您將使用什麼 SQL 命令來檢索具有電子郵件地址 `quincy@freecodecamp.org` 的所有用戶?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
id: 5e7b9f060b6c005b0e76f059
|
id: 5e7b9f060b6c005b0e76f059
|
||||||
title: More Conditional Structures
|
title: 更多條件結構
|
||||||
challengeType: 11
|
challengeType: 11
|
||||||
videoId: HdL82tAZR20
|
videoId: HdL82tAZR20
|
||||||
dashedName: more-conditional-structures
|
dashedName: more-conditional-structures
|
||||||
@ -8,17 +8,17 @@ dashedName: more-conditional-structures
|
|||||||
|
|
||||||
# --description--
|
# --description--
|
||||||
|
|
||||||
More resources:
|
更多資源:
|
||||||
|
|
||||||
\- [Exercise 1](https://www.youtube.com/watch?v=crLerB4ZxMI)
|
\- [練習 1](https://www.youtube.com/watch?v=crLerB4ZxMI)
|
||||||
|
|
||||||
\- [Exercise 2](https://www.youtube.com/watch?v=KJN3-7HH6yk)
|
\- [練習 2](https://www.youtube.com/watch?v=KJN3-7HH6yk)
|
||||||
|
|
||||||
# --question--
|
# --question--
|
||||||
|
|
||||||
## --text--
|
## --text--
|
||||||
|
|
||||||
Given the following code:
|
給出以下代碼:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
temp = "5 degrees"
|
temp = "5 degrees"
|
||||||
@ -28,7 +28,7 @@ cel = (fahr - 32.0) * 5.0 / 9.0
|
|||||||
print(cel)
|
print(cel)
|
||||||
```
|
```
|
||||||
|
|
||||||
Which line/lines should be surrounded by `try` block?
|
哪一行/幾行應該被 `try` 塊包圍?
|
||||||
|
|
||||||
## --answers--
|
## --answers--
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ Which line/lines should be surrounded by `try` block?
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
3,4
|
3、4
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ Which line/lines should be surrounded by `try` block?
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
None
|
無
|
||||||
|
|
||||||
## --video-solution--
|
## --video-solution--
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user