rebase against next
This commit is contained in:
@ -3,7 +3,8 @@
|
||||
"es6": true,
|
||||
"browser": true,
|
||||
"mocha": true,
|
||||
"node": true
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"root": true,
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -179,3 +179,6 @@ api-server/lib/*
|
||||
curriculum/dist
|
||||
curriculum/build
|
||||
client/static/_redirects
|
||||
|
||||
### UI Components ###
|
||||
tools/ui-components/dist
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import { ensureLowerCaseEmail } from './User-Identity';
|
||||
|
||||
test('returns lowercase email when one exists', () => {
|
||||
|
@ -26,7 +26,7 @@ import {
|
||||
renderSignInEmail
|
||||
} from '../utils';
|
||||
|
||||
import { blocklistedUsernames } from '../../server/utils/constants.js';
|
||||
import { blocklistedUsernames } from '../../../../config/constants';
|
||||
import { wrapHandledError } from '../../server/utils/create-handled-error.js';
|
||||
import { saveUser, observeMethod } from '../../server/utils/rx.js';
|
||||
import { getEmailSender } from '../../server/utils/url-utils';
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global it expect */
|
||||
|
||||
import { getFallbackFrontEndDate } from '../boot/certificate';
|
||||
import { fullStackChallenges } from './fixtures';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe xdescribe it expect jest */
|
||||
import { first, find } from 'lodash';
|
||||
|
||||
import {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global jest*/
|
||||
import { isEqual } from 'lodash';
|
||||
import { isEmail } from 'validator';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe it expect jest */
|
||||
import { mockReq as mockRequest, mockRes } from '../boot_tests/challenge.test';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe expect it */
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import { dayCount } from './date-utils';
|
||||
|
@ -1,7 +1,4 @@
|
||||
/* eslint-disable camelcase */
|
||||
/* global describe it expect */
|
||||
/* global jest*/
|
||||
|
||||
import axios from 'axios';
|
||||
import keys from '../../../../config/secrets';
|
||||
import {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe it expect */
|
||||
import {
|
||||
getAccessTokenFromRequest,
|
||||
errorTypes,
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe beforeEach expect it jest */
|
||||
import inMemoryCache from './in-memory-cache';
|
||||
|
||||
describe('InMemoryCache', () => {
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global describe expect it jest */
|
||||
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const { getReturnTo, normalizeParams } = require('./redirection');
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe it expect jest */
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import {
|
||||
availableLangs,
|
||||
langDisplayNames,
|
||||
|
@ -297,15 +297,15 @@
|
||||
}
|
||||
},
|
||||
"relational-databases": {
|
||||
"title": "Relational Databases",
|
||||
"title": "關係型數據庫",
|
||||
"intro": [
|
||||
"placeholder"
|
||||
"佔位符"
|
||||
],
|
||||
"blocks": {
|
||||
"learn-relational-databases": {
|
||||
"title": "Learn Relational Databases",
|
||||
"title": "學習關係型數據庫",
|
||||
"intro": [
|
||||
"placeholder"
|
||||
"佔位符"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -395,7 +395,8 @@
|
||||
"analytics": "條形圖和折線圖",
|
||||
"shield": "帶有複選標記的盾牌",
|
||||
"tensorflow": "Tensorflow 圖標",
|
||||
"algorithm": "分支節點"
|
||||
"algorithm": "分支節點",
|
||||
"magnifier": "magnifier"
|
||||
},
|
||||
"aria": {
|
||||
"fcc-logo": "freeCodeCamp 徽標",
|
||||
|
@ -297,15 +297,15 @@
|
||||
}
|
||||
},
|
||||
"relational-databases": {
|
||||
"title": "Relational Databases",
|
||||
"title": "关系型数据库",
|
||||
"intro": [
|
||||
"placeholder"
|
||||
"占位符"
|
||||
],
|
||||
"blocks": {
|
||||
"learn-relational-databases": {
|
||||
"title": "Learn Relational Databases",
|
||||
"title": "学习关系型数据库",
|
||||
"intro": [
|
||||
"placeholder"
|
||||
"占位符"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -395,7 +395,8 @@
|
||||
"analytics": "条形图和折线图",
|
||||
"shield": "带有复选标记的盾牌",
|
||||
"tensorflow": "Tensorflow 图标",
|
||||
"algorithm": "分支节点"
|
||||
"algorithm": "分支节点",
|
||||
"magnifier": "magnifier"
|
||||
},
|
||||
"aria": {
|
||||
"fcc-logo": "freeCodeCamp 徽标",
|
||||
|
@ -395,7 +395,8 @@
|
||||
"analytics": "A bar chart and line graph",
|
||||
"shield": "A shield with a checkmark",
|
||||
"tensorflow": "Tensorflow icon",
|
||||
"algorithm": "Branching nodes"
|
||||
"algorithm": "Branching nodes",
|
||||
"magnifier": "magnifier"
|
||||
},
|
||||
"aria": {
|
||||
"fcc-logo": "freeCodeCamp Logo",
|
||||
|
@ -395,7 +395,8 @@
|
||||
"analytics": "Un gráfico de barras y un gráfico de líneas",
|
||||
"shield": "Un escudo con una marca de verificación",
|
||||
"tensorflow": "Icono de Tensorflow",
|
||||
"algorithm": "Nodos ramificados"
|
||||
"algorithm": "Nodos ramificados",
|
||||
"magnifier": "magnifier"
|
||||
},
|
||||
"aria": {
|
||||
"fcc-logo": "Logo de freeCodeCamp",
|
||||
|
@ -395,7 +395,8 @@
|
||||
"analytics": "Un grafico a barre e un grafico a linea",
|
||||
"shield": "Uno scudo con un segno di spunta",
|
||||
"tensorflow": "Icona di Tensorflow",
|
||||
"algorithm": "Nodi con rami"
|
||||
"algorithm": "Nodi con rami",
|
||||
"magnifier": "magnifier"
|
||||
},
|
||||
"aria": {
|
||||
"fcc-logo": "Logo freeCodeCamp",
|
||||
@ -529,7 +530,7 @@
|
||||
"Caesars Cipher": "Cifrario di Cesare",
|
||||
"Telephone Number Validator": "Validatore Numero Di Telefono",
|
||||
"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 Treemap Diagram": "Visualizza i dati con una mappa ad albero",
|
||||
"Exercise Tracker": "Monitor degli esercizi",
|
||||
|
@ -395,7 +395,8 @@
|
||||
"analytics": "A bar chart and line graph",
|
||||
"shield": "A shield with a checkmark",
|
||||
"tensorflow": "Tensorflow icon",
|
||||
"algorithm": "Branching nodes"
|
||||
"algorithm": "Branching nodes",
|
||||
"magnifier": "magnifier"
|
||||
},
|
||||
"aria": {
|
||||
"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-function-bind": "7.14.5",
|
||||
"@babel/polyfill": "7.12.1",
|
||||
"@babel/preset-env": "7.14.5",
|
||||
"@babel/preset-env": "7.14.7",
|
||||
"@babel/preset-react": "7.14.5",
|
||||
"@babel/standalone": "7.14.6",
|
||||
"@babel/standalone": "7.14.7",
|
||||
"@fortawesome/fontawesome": "1.1.8",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.3",
|
||||
@ -70,17 +70,17 @@
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.6",
|
||||
"final-form": "4.20.2",
|
||||
"gatsby": "3.7.2",
|
||||
"gatsby-cli": "3.7.1",
|
||||
"gatsby": "3.8.0",
|
||||
"gatsby-cli": "3.8.0",
|
||||
"gatsby-plugin-advanced-sitemap": "1.6.0",
|
||||
"gatsby-plugin-create-client-paths": "3.7.1",
|
||||
"gatsby-plugin-manifest": "3.7.1",
|
||||
"gatsby-plugin-postcss": "4.7.1",
|
||||
"gatsby-plugin-react-helmet": "4.7.1",
|
||||
"gatsby-plugin-create-client-paths": "3.8.0",
|
||||
"gatsby-plugin-manifest": "3.8.0",
|
||||
"gatsby-plugin-postcss": "4.8.0",
|
||||
"gatsby-plugin-react-helmet": "4.8.0",
|
||||
"gatsby-plugin-remove-serviceworker": "1.0.0",
|
||||
"gatsby-remark-prismjs": "5.4.1",
|
||||
"gatsby-source-filesystem": "3.7.1",
|
||||
"gatsby-transformer-remark": "4.4.1",
|
||||
"gatsby-remark-prismjs": "5.5.0",
|
||||
"gatsby-source-filesystem": "3.8.0",
|
||||
"gatsby-transformer-remark": "4.5.0",
|
||||
"i18next": "20.3.2",
|
||||
"jquery": "3.6.0",
|
||||
"lodash": "4.17.21",
|
||||
@ -94,7 +94,7 @@
|
||||
"process": "0.11.10",
|
||||
"prop-types": "15.7.2",
|
||||
"psl": "1.8.0",
|
||||
"query-string": "7.0.0",
|
||||
"query-string": "7.0.1",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-final-form": "6.5.3",
|
||||
@ -130,22 +130,36 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "7.14.5",
|
||||
"@codesee/babel-plugin-instrument": "0.38.0",
|
||||
"@codesee/tracker": "0.38.0",
|
||||
"@codesee/babel-plugin-instrument": "0.41.0",
|
||||
"@codesee/tracker": "0.41.0",
|
||||
"@testing-library/jest-dom": "5.14.1",
|
||||
"@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/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",
|
||||
"babel-plugin-transform-imports": "2.0.0",
|
||||
"chokidar": "3.5.2",
|
||||
"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",
|
||||
"monaco-editor-webpack-plugin": "4.0.0",
|
||||
"react-test-renderer": "16.14.0",
|
||||
"redux-saga-test-plan": "4.0.1",
|
||||
"webpack": "5.39.1",
|
||||
"webpack": "5.40.0",
|
||||
"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/display-name */
|
||||
/* global jest */
|
||||
import React from 'react';
|
||||
|
||||
const reactI18next = jest.genMockFromModule('react-i18next');
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe it expect */
|
||||
import {
|
||||
wrapHandledError,
|
||||
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 ShallowRenderer from 'react-test-renderer/shallow';
|
||||
import envData from '../../../config/env.json';
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect jest */
|
||||
import React from 'react';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
|
||||
|
@ -3,10 +3,17 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import { Link, SkeletonSprite } from '../../helpers';
|
||||
import NavLogo from './NavLogo';
|
||||
import SearchBar from '../../search/searchBar/SearchBar';
|
||||
import MenuButton from './MenuButton';
|
||||
import NavLinks from './NavLinks';
|
||||
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 = ({
|
||||
displayMenu,
|
||||
@ -17,6 +24,14 @@ export const UniversalNav = ({
|
||||
fetchState
|
||||
}) => {
|
||||
const { pending } = fetchState;
|
||||
|
||||
const search =
|
||||
typeof window !== `undefined` && isLanding(window.location.pathname) ? (
|
||||
<SearchBarOptimized />
|
||||
) : (
|
||||
<SearchBar innerRef={searchBarRef} />
|
||||
);
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={'universal-nav' + (displayMenu ? ' expand-nav' : '')}
|
||||
@ -27,7 +42,7 @@ export const UniversalNav = ({
|
||||
'universal-nav-left' + (displayMenu ? ' display-search' : '')
|
||||
}
|
||||
>
|
||||
<SearchBar innerRef={searchBarRef} />
|
||||
{search}
|
||||
</div>
|
||||
<div className='universal-nav-middle'>
|
||||
<Link id='universal-nav-logo' to='/learn'>
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect jest */
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { Provider } from 'react-redux';
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect jest */
|
||||
|
||||
import React from 'react';
|
||||
import { useStaticQuery } from 'gatsby';
|
||||
import { render } from '@testing-library/react';
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import createExternalRedirect from './createExternalRedirects';
|
||||
|
||||
describe('createExternalRedirects', () => {
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import createLanguageRedirect from './createLanguageRedirect';
|
||||
|
||||
describe('createLanguageRedirect for clientLocale === english', () => {
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global jest, expect */
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import React from 'react';
|
||||
import renderer, { ReactTestRendererJSON } from 'react-test-renderer';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import React from 'react';
|
||||
import { render, cleanup } from '@testing-library/react';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect jest */
|
||||
import React from 'react';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
|
||||
|
@ -21,7 +21,6 @@ import { flashMessageSelector, removeFlashMessage } from '../Flash/redux';
|
||||
|
||||
import { isBrowser } from '../../../utils';
|
||||
|
||||
import WithInstantSearch from '../search/WithInstantSearch';
|
||||
import OfflineWarning from '../OfflineWarning';
|
||||
import Flash from '../Flash';
|
||||
import Header from '../Header';
|
||||
@ -201,17 +200,15 @@ class DefaultLayout extends Component {
|
||||
/>
|
||||
<style>{fontawesome.dom.css()}</style>
|
||||
</Helmet>
|
||||
<WithInstantSearch>
|
||||
<div className={`default-layout`}>
|
||||
<Header fetchState={fetchState} user={user} />
|
||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||
{hasMessage && flashMessage ? (
|
||||
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
{showFooter && <Footer />}
|
||||
</WithInstantSearch>
|
||||
<div className={`default-layout`}>
|
||||
<Header fetchState={fetchState} user={user} />
|
||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||
{hasMessage && flashMessage ? (
|
||||
<Flash flashMessage={flashMessage} onClose={removeFlashMessage} />
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
{showFooter && <Footer />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect jest */
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect jest */
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import TimeLine from './TimeLine';
|
||||
|
@ -9,6 +9,8 @@ import { isEqual } from 'lodash-es';
|
||||
import { withTranslation } from 'react-i18next';
|
||||
import { searchPageUrl } from '../../../utils/algolia-locale-setup';
|
||||
|
||||
import WithInstantSearch from '../WithInstantSearch';
|
||||
|
||||
import {
|
||||
isSearchDropdownEnabledSelector,
|
||||
isSearchBarFocusedSelector,
|
||||
@ -177,33 +179,39 @@ export class SearchBar extends Component {
|
||||
const placeholder = t('search.placeholder');
|
||||
|
||||
return (
|
||||
<div className='fcc_searchBar' data-testid='fcc_searchBar' ref={innerRef}>
|
||||
<HotKeys handlers={this.keyHandlers} keyMap={this.keyMap}>
|
||||
<div className='fcc_search_wrapper'>
|
||||
<label className='fcc_sr_only' htmlFor='fcc_instantsearch'>
|
||||
{t('search.label')}
|
||||
</label>
|
||||
<ObserveKeys except={['Space']}>
|
||||
<SearchBox
|
||||
focusShortcuts={[83, 191]}
|
||||
onChange={this.handleChange}
|
||||
onFocus={this.handleFocus}
|
||||
onSubmit={this.handleSearch}
|
||||
showLoadingIndicator={false}
|
||||
translations={{ placeholder }}
|
||||
/>
|
||||
</ObserveKeys>
|
||||
{isDropdownEnabled && isSearchFocused && (
|
||||
<SearchHits
|
||||
handleHits={this.handleHits}
|
||||
handleMouseEnter={this.handleMouseEnter}
|
||||
handleMouseLeave={this.handleMouseLeave}
|
||||
selectedIndex={index}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div>
|
||||
<WithInstantSearch>
|
||||
<div
|
||||
className='fcc_searchBar'
|
||||
data-testid='fcc_searchBar'
|
||||
ref={innerRef}
|
||||
>
|
||||
<HotKeys handlers={this.keyHandlers} keyMap={this.keyMap}>
|
||||
<div className='fcc_search_wrapper'>
|
||||
<label className='fcc_sr_only' htmlFor='fcc_instantsearch'>
|
||||
{t('search.label')}
|
||||
</label>
|
||||
<ObserveKeys except={['Space']}>
|
||||
<SearchBox
|
||||
focusShortcuts={[83, 191]}
|
||||
onChange={this.handleChange}
|
||||
onFocus={this.handleFocus}
|
||||
onSubmit={this.handleSearch}
|
||||
showLoadingIndicator={false}
|
||||
translations={{ placeholder }}
|
||||
/>
|
||||
</ObserveKeys>
|
||||
{isDropdownEnabled && isSearchFocused && (
|
||||
<SearchHits
|
||||
handleHits={this.handleHits}
|
||||
handleMouseEnter={this.handleMouseEnter}
|
||||
handleMouseLeave={this.handleMouseLeave}
|
||||
selectedIndex={index}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</HotKeys>
|
||||
</div>
|
||||
</WithInstantSearch>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global jest, expect */
|
||||
import React from 'react';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
|
||||
|
@ -46,7 +46,7 @@ const CustomHits = connectHits(
|
||||
|
||||
return (
|
||||
<div className='ais-Hits'>
|
||||
<ul className='ais-Hits-list'>
|
||||
<ul className='ais-Hits-list' data-cy='ais-Hits-list'>
|
||||
{allHits.map((hit, i) => (
|
||||
<li
|
||||
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 { render } from '@testing-library/react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect jest */
|
||||
import React from 'react';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import toLearnPath from '../utils/to-learn-path';
|
||||
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 { ActionsObservable, StateObservable } from 'redux-observable';
|
||||
import failedUpdatesEpic from './failed-updates-epic';
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global jest */
|
||||
|
||||
import { types } from '.';
|
||||
import { createGaSaga } from './ga-saga';
|
||||
import ga from '../analytics';
|
||||
|
@ -84,69 +84,79 @@ class DesktopLayout extends Component {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{projectBasedChallenge && (
|
||||
<ActionRow switchDisplayTab={this.switchDisplayTab} {...this.state} />
|
||||
)}
|
||||
<ReflexContainer className='desktop-layout' orientation='vertical'>
|
||||
{!projectBasedChallenge && (
|
||||
<ReflexElement
|
||||
flex={instructionPane.flex}
|
||||
name='instructionPane'
|
||||
{...resizeProps}
|
||||
>
|
||||
{instructions}
|
||||
</ReflexElement>
|
||||
<ReflexContainer className='desktop-layout' orientation='horizontal'>
|
||||
{projectBasedChallenge && (
|
||||
<ActionRow
|
||||
switchDisplayTab={this.switchDisplayTab}
|
||||
{...this.state}
|
||||
/>
|
||||
)}
|
||||
{!projectBasedChallenge && (
|
||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||
)}
|
||||
|
||||
<ReflexElement
|
||||
flex={editorPane.flex}
|
||||
name='editorPane'
|
||||
{...resizeProps}
|
||||
>
|
||||
{challengeFile && showUpcomingChanges && !hasEditableBoundries && (
|
||||
<EditorTabs />
|
||||
)}
|
||||
{challengeFile && (
|
||||
<ReflexContainer key={challengeFile.key} orientation='horizontal'>
|
||||
<ReflexElement flex={8} {...reflexProps} {...resizeProps}>
|
||||
<ReflexContainer orientation='vertical'>
|
||||
{!projectBasedChallenge && (
|
||||
<ReflexElement
|
||||
flex={codePane.flex}
|
||||
name='codePane'
|
||||
{...reflexProps}
|
||||
flex={instructionPane.flex}
|
||||
name='instructionPane'
|
||||
{...resizeProps}
|
||||
>
|
||||
{<Fragment>{editor}</Fragment>}
|
||||
{instructions}
|
||||
</ReflexElement>
|
||||
{isConsoleDisplayable && (
|
||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||
)}
|
||||
{isConsoleDisplayable && (
|
||||
<ReflexElement
|
||||
flex={testsPane.flex}
|
||||
name='testsPane'
|
||||
{...reflexProps}
|
||||
{...resizeProps}
|
||||
)}
|
||||
{!projectBasedChallenge && (
|
||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||
)}
|
||||
|
||||
<ReflexElement
|
||||
flex={editorPane.flex}
|
||||
name='editorPane'
|
||||
{...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>
|
||||
{isPreviewDisplayable && (
|
||||
<ReflexSplitter propagate={true} {...resizeProps} />
|
||||
)}
|
||||
{isPreviewDisplayable && (
|
||||
<ReflexElement
|
||||
flex={previewPane.flex}
|
||||
name='previewPane'
|
||||
{...resizeProps}
|
||||
>
|
||||
{preview}
|
||||
</ReflexElement>
|
||||
)}
|
||||
</ReflexContainer>
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect jest */
|
||||
|
||||
import { getCompletedPercent } from './CompletionModal';
|
||||
|
||||
jest.mock('../../../analytics');
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global jest, expect */
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect */
|
||||
import { findIndexHtml } from './builders.js';
|
||||
|
||||
const withHTML = [
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import { transformEditorLink } from '../utils';
|
||||
|
||||
describe('create-question-epic', () => {
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import envData from '../../../../../config/env.json';
|
||||
|
||||
const { forumLocation } = envData;
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect, jest */
|
||||
|
||||
import createWorker from './worker-executor';
|
||||
|
||||
function mockWorker({ init, postMessage, terminate } = {}) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global describe it expect */
|
||||
import { cssString } from './__fixtures/curriculum-helpers-css';
|
||||
import CSSHelp from './css-help';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global describe it expect */
|
||||
|
||||
import __testHelpers, { removeJSComments } from './curriculum-helpers';
|
||||
import jsTestValues from './__fixtures/curriculum-helpers-javascript';
|
||||
import cssTestValues from './__fixtures/curriculum-helpers-css';
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* global expect BigInt */
|
||||
/* global BigInt */
|
||||
|
||||
const { format } = require('./format');
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect jest */
|
||||
import { isObject } from 'lodash-es';
|
||||
import {
|
||||
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": {
|
||||
"target": "es2020",
|
||||
"module": "es2020",
|
||||
@ -11,7 +16,12 @@
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true
|
||||
"types": [
|
||||
"node",
|
||||
"jest",
|
||||
"@testing-library/jest-dom"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import {
|
||||
DefaultLayout
|
||||
} from '../../src/components/layouts';
|
||||
import FourOhFourPage from '../../src/pages/404';
|
||||
import { isChallenge } from '../../src/utils/path-parsers';
|
||||
|
||||
export default function layoutSelector({ element, props }) {
|
||||
const {
|
||||
@ -18,32 +19,23 @@ export default function layoutSelector({ element, props }) {
|
||||
{element}
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
if (/\/certification\//.test(pathname)) {
|
||||
} else if (/\/certification\//.test(pathname)) {
|
||||
return (
|
||||
<CertificationLayout pathname={pathname}>{element}</CertificationLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const splitPath = pathname.split('/').filter(x => x);
|
||||
|
||||
const isChallenge =
|
||||
(splitPath.length === 4 && splitPath[0]) === 'learn' ||
|
||||
(splitPath.length === 5 && splitPath[1]) === 'learn';
|
||||
|
||||
if (isChallenge) {
|
||||
} else if (isChallenge(pathname)) {
|
||||
return (
|
||||
<DefaultLayout pathname={pathname} showFooter={false}>
|
||||
{element}
|
||||
</DefaultLayout>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<DefaultLayout pathname={pathname} showFooter={true}>
|
||||
{element}
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DefaultLayout pathname={pathname} showFooter={true}>
|
||||
{element}
|
||||
</DefaultLayout>
|
||||
);
|
||||
}
|
||||
|
||||
layoutSelector.propTypes = {
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global expect jest */
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* global expect */
|
||||
|
||||
import { injectConditionalTags } from './tags';
|
||||
|
||||
describe('Tags', () => {
|
||||
|
@ -4,8 +4,44 @@ for (let i = 0; i < 26; 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 = [
|
||||
...alphabet.split(''),
|
||||
...i18nConstants,
|
||||
'about',
|
||||
'academic-honesty',
|
||||
'account',
|
||||
@ -85,40 +121,6 @@ let blocklist = [
|
||||
'user',
|
||||
'username',
|
||||
'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
|
||||
'.htaccess',
|
||||
'.htpasswd',
|
||||
@ -647,4 +649,5 @@ let blocklist = [
|
||||
'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--
|
||||
|
||||
|
@ -17,7 +17,7 @@ Animal.prototype.eat = function() {
|
||||
};
|
||||
```
|
||||
|
||||
在這一節以及下一節挑戰中我們將學習如何在 `Bird` 和 `Dog` 中重用 `Animal's` 中的方法,而無需重新定義它們。 這裏我們會用到構造函數的繼承特性。 這一節挑戰中我們學習第一步:創建一個超類 `supertype`(或者叫父類)的實例。 你已經學會了一種創建 `Animal` 實例的方法,即使用 `new` 操作符:
|
||||
在這一節以及下一節挑戰中我們將學習如何在 `Bird` 和 `Dog` 中重用 `Animal` 中的方法,而無需重新定義它們。 這裏我們會用到構造函數的繼承特性。 這一節挑戰中我們學習第一步:創建一個超類 `supertype`(或者叫父類)的實例。 你已經學會了一種創建 `Animal` 實例的方法,即使用 `new` 操作符:
|
||||
|
||||
```js
|
||||
let animal = new Animal();
|
||||
@ -29,7 +29,7 @@ let animal = new Animal();
|
||||
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
|
||||
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()` 是定義在這裏嗎? 不是。
|
||||
2. `Bird` => `eat()` 是定義在這裏嗎? => 是的。 執行它並停止往上搜索。
|
||||
|
@ -19,7 +19,7 @@ let duck = new Bird();
|
||||
duck.constructor
|
||||
```
|
||||
|
||||
但是 `duck` 和其他所有 `Bird` 的實例都應該表明它們是由 `Bird` 創建的,而不是由 `Animal` 創建的。 爲此,你可以手動把 `Bird's` 的 constructor 屬性設置爲 `Bird` 對象:
|
||||
但是 `duck` 和其他所有 `Bird` 的實例都應該表明它們是由 `Bird` 創建的,而不是由 `Animal` 創建的。 爲此,你可以手動將 `Bird` 的構造函數屬性設置爲 `Bird` 對象:
|
||||
|
||||
```js
|
||||
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
|
||||
Bird.prototype.numLegs = 2;
|
||||
@ -45,7 +45,7 @@ assert(beagle.numLegs !== undefined);
|
||||
assert(typeof beagle.numLegs === 'number');
|
||||
```
|
||||
|
||||
`numLegs` 應該是一個 `prototype` 屬性而不是一個 `own` 屬性。
|
||||
`numLegs` 應該是一個 `prototype` 屬性,而不是一個自身屬性。
|
||||
|
||||
```js
|
||||
assert(beagle.hasOwnProperty('numLegs') === false);
|
||||
|
@ -16,7 +16,7 @@ dashedName: create-a-controlled-form
|
||||
|
||||
我們增加了一個提交表單的按鈕。 可以看到它的 `type` 被設置爲 `submit`,表明它是控制表單提交的按鈕。 在 `form` 中添加 `input` 元素,並像上個挑戰一樣設置其 `value` 和 `onChange()` 屬性。 然後,應該完成 `handleSubmit` 方法,以便將組件 state 屬性 `submit` 設置爲本地 `state` 下的當前輸入值。
|
||||
|
||||
**注意:** 還必須在提交處理程序中調用 `event.preventDefault()`,以防止默認的表單提交行爲刷新網頁。
|
||||
**注意:** 還必須在提交處理程序中調用 `event.preventDefault()`,以防止將會刷新網頁的默認的表單提交行爲。 爲了便於學員操作,默認行爲在這裏被禁用,以防止重置挑戰的代碼。
|
||||
|
||||
最後,在 `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
|
||||
(() => {
|
||||
|
@ -18,8 +18,7 @@ dashedName: personal-library
|
||||
|
||||
# --instructions--
|
||||
|
||||
1. 將的 MongoDB 連接字符串添加到 `.env` 中(沒有引號),`DB`
|
||||
示例: `DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
|
||||
1. 將你的 MongoDB 連接字符串添加到 `.env` 中,作爲 `DB` 示例:`DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib`
|
||||
2. 在 `.env` 文件中設置 `NODE_ENV` 爲 `test`中,沒有引號
|
||||
3. 需要在 `routes/api.js` 中創建所有路由
|
||||
4. 在 `tests/2_functional-tests.js` 中創建所有的功能測試
|
||||
@ -66,7 +65,7 @@ async (getUserInput) => {
|
||||
let a = $.post(url, { title: 'Faux Book A' });
|
||||
let b = $.post(url, { title: 'Faux Book B' });
|
||||
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);
|
||||
assert.isArray(data);
|
||||
assert.isAtLeast(data.length, 3);
|
||||
@ -214,8 +213,8 @@ async (getUserInput) => {
|
||||
|
||||
```js
|
||||
/**
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Backend challenges don't need solutions,
|
||||
because they would need to be tested against a full working project.
|
||||
Please check our contributing guidelines to learn more.
|
||||
*/
|
||||
```
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f060b6c005b0e76f05b
|
||||
title: Build your own Functions
|
||||
title: 編寫你自己的函數
|
||||
challengeType: 11
|
||||
videoId: nLDychdBwUg
|
||||
dashedName: build-your-own-functions
|
||||
@ -8,15 +8,15 @@ dashedName: build-your-own-functions
|
||||
|
||||
# --description--
|
||||
|
||||
More resources:
|
||||
更多資源
|
||||
|
||||
\- [Exercise](https://www.youtube.com/watch?v=ksvGhDsjtpw)
|
||||
\- [練習](https://www.youtube.com/watch?v=ksvGhDsjtpw)
|
||||
|
||||
# --question--
|
||||
|
||||
## --text--
|
||||
|
||||
What will the following Python program print out?:
|
||||
下面這個 Python 程序會打印什麼?:
|
||||
|
||||
```python
|
||||
def fred():
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f0b0b6c005b0e76f06d
|
||||
title: Comparing and Sorting Tuples
|
||||
title: Tuples 的比較和排序
|
||||
challengeType: 11
|
||||
videoId: dZXzBXUxxCs
|
||||
dashedName: comparing-and-sorting-tuples
|
||||
@ -8,15 +8,15 @@ dashedName: comparing-and-sorting-tuples
|
||||
|
||||
# --description--
|
||||
|
||||
More resources:
|
||||
更多資源:
|
||||
|
||||
\- [Exercise](https://www.youtube.com/watch?v=EhQxwzyT16E)
|
||||
\- [練習](https://www.youtube.com/watch?v=EhQxwzyT16E)
|
||||
|
||||
# --question--
|
||||
|
||||
## --text--
|
||||
|
||||
Which does the same thing as the following code?:
|
||||
哪個代碼與示例代碼完成相同的功能?
|
||||
|
||||
```python
|
||||
lst = []
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f050b6c005b0e76f058
|
||||
title: Conditional Execution
|
||||
title: 根據條件執行
|
||||
challengeType: 11
|
||||
videoId: gz_IfIsZQtc
|
||||
dashedName: conditional-execution
|
||||
@ -10,7 +10,7 @@ dashedName: conditional-execution
|
||||
|
||||
## --text--
|
||||
|
||||
Which code is indented correctly to print "Yes" if x = 0 and y = 10?
|
||||
以下哪個代碼縮進,能夠滿足當“x=0”和“y=10”的時,打印 "Yes"?
|
||||
|
||||
## --answers--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f6a0b6c005b0e76f097
|
||||
title: 'Data Visualization: Mailing Lists'
|
||||
title: '數據可視化:郵件列表'
|
||||
challengeType: 11
|
||||
videoId: RYdW660KkaQ
|
||||
dashedName: data-visualization-mailing-lists
|
||||
@ -8,27 +8,27 @@ dashedName: data-visualization-mailing-lists
|
||||
|
||||
# --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--
|
||||
|
||||
## --text--
|
||||
|
||||
Which is a common JavaScript visualization library?
|
||||
哪個是常見的 JavaScript 可視化庫?
|
||||
|
||||
## --answers--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f6a0b6c005b0e76f096
|
||||
title: 'Data Visualization: Page Rank'
|
||||
title: '數據可視化:頁面排名'
|
||||
challengeType: 11
|
||||
videoId: 6-w_qIUwaxU
|
||||
dashedName: data-visualization-page-rank
|
||||
@ -10,19 +10,19 @@ dashedName: data-visualization-page-rank
|
||||
|
||||
## --text--
|
||||
|
||||
How does the PageRank algorithm work?
|
||||
頁面排名算法是如何工作的?
|
||||
|
||||
## --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--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f0a0b6c005b0e76f069
|
||||
title: Dictionaries and Loops
|
||||
title: 字典和循環
|
||||
challengeType: 11
|
||||
videoId: EEmekKiKG70
|
||||
dashedName: dictionaries-and-loops
|
||||
@ -8,15 +8,15 @@ dashedName: dictionaries-and-loops
|
||||
|
||||
# --description--
|
||||
|
||||
More resources:
|
||||
更多資源:
|
||||
|
||||
\- [Exercise](https://www.youtube.com/watch?v=PrhZ9qwBDD8)
|
||||
\- [練習](https://www.youtube.com/watch?v=PrhZ9qwBDD8)
|
||||
|
||||
# --question--
|
||||
|
||||
## --text--
|
||||
|
||||
What will the following code print?:
|
||||
以下代碼將打印什麼?
|
||||
|
||||
```python
|
||||
counts = { 'chuck' : 1 , 'annie' : 42, 'jan': 100}
|
||||
@ -42,7 +42,7 @@ jan 100</pre>
|
||||
|
||||
---
|
||||
|
||||
<pre>[Error]</pre>
|
||||
<pre>[Error](錯誤)</pre>
|
||||
|
||||
## --video-solution--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f070b6c005b0e76f061
|
||||
title: Intermediate Strings
|
||||
title: 中間字符串
|
||||
challengeType: 11
|
||||
videoId: KgT_fYLXnyk
|
||||
dashedName: intermediate-strings
|
||||
@ -8,15 +8,15 @@ dashedName: intermediate-strings
|
||||
|
||||
# --description--
|
||||
|
||||
More resources:
|
||||
更多資料:
|
||||
|
||||
\- [Exercise](https://www.youtube.com/watch?v=1bSqHot-KwE)
|
||||
[練習](https://www.youtube.com/watch?v=1bSqHot-KwE)
|
||||
|
||||
# --question--
|
||||
|
||||
## --text--
|
||||
|
||||
What is the value of i in the following code?
|
||||
下面的代碼中 i 的值是什麼?
|
||||
|
||||
```python
|
||||
word = "bananana"
|
||||
@ -37,7 +37,7 @@ nanana
|
||||
|
||||
---
|
||||
|
||||
True
|
||||
True(真)
|
||||
|
||||
---
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e6a54c358d3af90110a60a3
|
||||
title: 'Introduction: Elements of Python'
|
||||
title: '導言:Python 的元素'
|
||||
challengeType: 11
|
||||
videoId: aRY_xjL35v0
|
||||
dashedName: introduction-elements-of-python
|
||||
@ -10,7 +10,7 @@ dashedName: introduction-elements-of-python
|
||||
|
||||
## --text--
|
||||
|
||||
What will the following program print out:
|
||||
以下代碼將打印出什麼?
|
||||
|
||||
```python
|
||||
x = 43
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e6a54af58d3af90110a60a1
|
||||
title: 'Introduction: Hardware Architecture'
|
||||
title: '導言:硬件架構'
|
||||
challengeType: 11
|
||||
videoId: H6qtjRTfSog
|
||||
dashedName: introduction-hardware-architecture
|
||||
@ -10,19 +10,19 @@ dashedName: introduction-hardware-architecture
|
||||
|
||||
## --text--
|
||||
|
||||
Where are your programs stored when they are running?
|
||||
當你的程序運作時,它被存儲在哪裏?
|
||||
|
||||
## --answers--
|
||||
|
||||
Hard Drive.
|
||||
硬盤。
|
||||
|
||||
---
|
||||
|
||||
Memory.
|
||||
內存。
|
||||
|
||||
---
|
||||
|
||||
Central Processing Unit.
|
||||
中央處理器。
|
||||
|
||||
## --video-solution--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e6a54ba58d3af90110a60a2
|
||||
title: 'Introduction: Python as a Language'
|
||||
title: '導言:Python 這門編程語言'
|
||||
challengeType: 11
|
||||
videoId: 0QeGbZNS_bY
|
||||
dashedName: introduction-python-as-a-language
|
||||
@ -10,7 +10,7 @@ dashedName: introduction-python-as-a-language
|
||||
|
||||
## --text--
|
||||
|
||||
What will print out after running these two lines of code:
|
||||
運行這兩行代碼後打印出什麼樣的內容:
|
||||
|
||||
```python
|
||||
x = 6
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e6a54a558d3af90110a60a0
|
||||
title: 'Introduction: Why Program?'
|
||||
title: '導言:爲何編程?'
|
||||
challengeType: 11
|
||||
videoId: 3muQV-Im3Z0
|
||||
dashedName: introduction-why-program
|
||||
@ -8,29 +8,29 @@ dashedName: introduction-why-program
|
||||
|
||||
# --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--
|
||||
|
||||
## --text--
|
||||
|
||||
Who should learn to program?
|
||||
誰應該學習編程?
|
||||
|
||||
## --answers--
|
||||
|
||||
College students.
|
||||
大學生。
|
||||
|
||||
---
|
||||
|
||||
People who want to become software developers.
|
||||
想成爲軟件開發者的人。
|
||||
|
||||
---
|
||||
|
||||
Everyone.
|
||||
所有人。
|
||||
|
||||
## --video-solution--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f070b6c005b0e76f05d
|
||||
title: 'Iterations: Definite Loops'
|
||||
title: '迭代:定義循環'
|
||||
challengeType: 11
|
||||
videoId: hiRTRAqNlpE
|
||||
dashedName: iterations-definite-loops
|
||||
@ -10,7 +10,7 @@ dashedName: iterations-definite-loops
|
||||
|
||||
## --text--
|
||||
|
||||
How many lines will the following code print?:
|
||||
以下代碼將打印多少行?
|
||||
|
||||
```python
|
||||
for i in [2,1,5]:
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f070b6c005b0e76f05e
|
||||
title: 'Iterations: Loop Idioms'
|
||||
title: '迭代:循環成語'
|
||||
challengeType: 11
|
||||
videoId: AelGAcoMXbI
|
||||
dashedName: iterations-loop-idioms
|
||||
@ -10,7 +10,7 @@ dashedName: iterations-loop-idioms
|
||||
|
||||
## --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
|
||||
smallest = None
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f070b6c005b0e76f05f
|
||||
title: 'Iterations: More Patterns'
|
||||
title: '迭代:更多模式'
|
||||
challengeType: 11
|
||||
videoId: 9Wtqo6vha1M
|
||||
dashedName: iterations-more-patterns
|
||||
@ -8,15 +8,15 @@ dashedName: iterations-more-patterns
|
||||
|
||||
# --description--
|
||||
|
||||
More resources:
|
||||
更多資源:
|
||||
|
||||
\- [Exercise](https://www.youtube.com/watch?v=kjxXZQw0uPg)
|
||||
\- [練習](https://www.youtube.com/watch?v=kjxXZQw0uPg)
|
||||
|
||||
# --question--
|
||||
|
||||
## --text--
|
||||
|
||||
Which of these evaluates to False?
|
||||
其中哪一個評估爲 False(假)?
|
||||
|
||||
## --answers--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f060b6c005b0e76f05c
|
||||
title: Loops and Iterations
|
||||
title: 循環和迭代
|
||||
challengeType: 11
|
||||
videoId: dLA-szNRnUY
|
||||
dashedName: loops-and-iterations
|
||||
@ -10,7 +10,7 @@ dashedName: loops-and-iterations
|
||||
|
||||
## --text--
|
||||
|
||||
What will the following code print out?:
|
||||
這些代碼會打印出什麼?
|
||||
|
||||
```python
|
||||
n = 0
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f170b6c005b0e76f08b
|
||||
title: Make a Relational Database
|
||||
title: 建立關係數據庫
|
||||
challengeType: 11
|
||||
videoId: MQ5z4bdF92U
|
||||
dashedName: make-a-relational-database
|
||||
@ -10,7 +10,7 @@ dashedName: make-a-relational-database
|
||||
|
||||
## --text--
|
||||
|
||||
What SQL command would you use to retrieve all users that have the email address `quincy@freecodecamp.org`?
|
||||
您將使用什麼 SQL 命令來檢索具有電子郵件地址 `quincy@freecodecamp.org` 的所有用戶?
|
||||
|
||||
## --answers--
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
---
|
||||
id: 5e7b9f060b6c005b0e76f059
|
||||
title: More Conditional Structures
|
||||
title: 更多條件結構
|
||||
challengeType: 11
|
||||
videoId: HdL82tAZR20
|
||||
dashedName: more-conditional-structures
|
||||
@ -8,17 +8,17 @@ dashedName: more-conditional-structures
|
||||
|
||||
# --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--
|
||||
|
||||
## --text--
|
||||
|
||||
Given the following code:
|
||||
給出以下代碼:
|
||||
|
||||
```python
|
||||
temp = "5 degrees"
|
||||
@ -28,7 +28,7 @@ cel = (fahr - 32.0) * 5.0 / 9.0
|
||||
print(cel)
|
||||
```
|
||||
|
||||
Which line/lines should be surrounded by `try` block?
|
||||
哪一行/幾行應該被 `try` 塊包圍?
|
||||
|
||||
## --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--
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user