├── public
├── favicon.ico
├── manifest.json
└── index.html
├── src
├── utils
│ ├── __mocks__
│ │ └── terms.js
│ ├── GoogleAnalytics.js
│ └── terms.js
├── components
│ ├── Highlight
│ │ ├── Highlight.css
│ │ ├── __snapshots__
│ │ │ └── Highlight.test.js.snap
│ │ ├── Highlight.test.js
│ │ └── Highlight.js
│ ├── Header
│ │ ├── __snapshots__
│ │ │ └── Header.test.js.snap
│ │ ├── Header.test.js
│ │ ├── logo.svg
│ │ ├── Header.js
│ │ └── Header.css
│ ├── ScrollToTop
│ │ ├── __snapshots__
│ │ │ └── ScrollToTop.test.js.snap
│ │ ├── ScrollToTop.test.js
│ │ └── ScrollToTop.js
│ ├── TermList
│ │ ├── __snapshots__
│ │ │ └── TermList.test.js.snap
│ │ ├── TermList.test.js
│ │ ├── TermList.js
│ │ └── TermList.css
│ ├── TermCard
│ │ ├── __snapshots__
│ │ │ └── TermCard.test.js.snap
│ │ ├── TermCard.test.js
│ │ ├── TermCard.js
│ │ └── TermCard.css
│ └── Search
│ │ ├── Search.test.js
│ │ ├── Search.css
│ │ ├── __snapshots__
│ │ └── Search.test.js.snap
│ │ └── Search.js
├── index.js
├── shared.css
├── App.css
├── App.test.js
├── screens
│ ├── Term
│ │ ├── Term.test.js
│ │ ├── __snapshots__
│ │ │ └── Term.test.js.snap
│ │ ├── Term.css
│ │ └── Term.js
│ └── Home
│ │ ├── __snapshots__
│ │ └── Home.test.js.snap
│ │ ├── Home.test.js
│ │ ├── Home.css
│ │ └── Home.js
├── __snapshots__
│ └── App.test.js.snap
├── App.js
├── sampleTerms.json
└── logo.svg
├── .travis.yml
├── app.json
├── config
├── jest
│ ├── fileTransform.js
│ └── cssTransform.js
├── data.js
├── polyfills.js
├── eslint.js
├── paths.js
├── env.js
├── webpackDevServer.config.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── .gitignore
├── scripts
├── test.js
├── start.js
└── build.js
├── LICENSE
├── README.md
└── package.json
/public/favicon.ico:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/utils/__mocks__/terms.js:
--------------------------------------------------------------------------------
1 | export const departments = [];
2 |
3 | export default [];
4 |
--------------------------------------------------------------------------------
/src/components/Highlight/Highlight.css:
--------------------------------------------------------------------------------
1 | .mark {
2 | color: var(--color-white);
3 | background-color: var(--color-gold);
4 | }
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
4 | cache: yarn
5 | jobs:
6 | include:
7 | - stage: test
8 | script: eslint
9 |
--------------------------------------------------------------------------------
/src/components/Header/__snapshots__/Header.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Header component renders correctly 1`] = `null`;
4 |
--------------------------------------------------------------------------------
/src/components/Highlight/__snapshots__/Highlight.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Highlight component renders correctly 1`] = `
`;
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import App from './App';
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/__snapshots__/ScrollToTop.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`ScrollToTop component renders correctly 1`] = `
`;
4 |
--------------------------------------------------------------------------------
/src/shared.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: block;
3 | padding: 2rem;
4 | position: relative;
5 | min-height: 100vh;
6 | }
7 |
8 | @media(min-width: 40rem) {
9 | .container {
10 | padding: 4rem;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils/GoogleAnalytics.js:
--------------------------------------------------------------------------------
1 | import GA from 'react-ga';
2 |
3 | export default (props) => {
4 | GA.set({ page: props.location.pathname + props.location.search });
5 | GA.pageview(props.location.pathname + props.location.search);
6 | return null;
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/TermList/__snapshots__/TermList.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`TermList component renders correctly 1`] = `
4 |
9 | `;
10 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Lingo",
3 | "description": "Appear Here's Data Dictionary",
4 | "repository": "https://github.com/appearhere/lingo",
5 | "logo": "https://logos.appearhere.co.uk/brackets/64x64/FFFFFF.png",
6 | "keywords": ["node", "react", "data-dictonary"]
7 | }
8 |
--------------------------------------------------------------------------------
/src/utils/terms.js:
--------------------------------------------------------------------------------
1 | /* global __TERMS__:true */
2 |
3 | export const departments = __TERMS__
4 | .reduce((acc, { department }) => {
5 | if (acc.indexOf(department) === -1) acc.push(department);
6 | return acc;
7 | }, [])
8 | .sort();
9 |
10 | export default __TERMS__;
11 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .app {
6 | box-sizing: border-box;
7 | -webkit-font-smoothing: antialiased;
8 | margin: 0;
9 | font-family: 'Avenir Next W01', 'Helvetica Neue', 'Helvetica', 'sans-serif';
10 | color: var(--color-black);
11 | height: 100%;
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/TermCard/__snapshots__/TermCard.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`TermCard component renders correctly 1`] = `
4 |
9 |
12 |
15 |
16 | `;
17 |
--------------------------------------------------------------------------------
/src/components/TermList/TermList.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import TermList from './TermList';
4 |
5 | describe('TermList component', () => {
6 | it('renders correctly', () => {
7 | const tree = renderer
8 | .create( )
9 | .toJSON();
10 | expect(tree).toMatchSnapshot();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`;
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/Highlight/Highlight.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import Highlight from './Highlight';
5 |
6 | describe('Highlight component', () => {
7 | it('renders correctly', () => {
8 | const tree = renderer
9 | .create( )
10 | .toJSON();
11 | expect(tree).toMatchSnapshot();
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # testing
7 | /coverage
8 |
9 | # production
10 | /build
11 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 | src/terms.json
23 |
--------------------------------------------------------------------------------
/config/data.js:
--------------------------------------------------------------------------------
1 | const paths = require('./paths');
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 |
6 | const terms = path.resolve(paths.appSrc, 'terms.json');
7 | const sampleTerms = path.resolve(paths.appSrc, 'sampleTerms.json');
8 |
9 | const termsFile = fs.existsSync(terms) ? terms : sampleTerms;
10 |
11 | module.exports = {
12 | terms: fs.readFileSync(termsFile).toString(),
13 | };
14 |
--------------------------------------------------------------------------------
/src/components/Search/Search.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import { StaticRouter } from 'react-router';
4 |
5 | import Search from './Search';
6 |
7 | describe('Search component', () => {
8 | it('renders correctly', () => {
9 | const tree = renderer
10 | .create( )
11 | .toJSON();
12 | expect(tree).toMatchSnapshot();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 |
4 | import App from './App';
5 |
6 | jest.mock('@appearhere/react-stickynode');
7 | jest.mock('react-ga');
8 | jest.mock('./utils/terms');
9 |
10 | describe('App component', () => {
11 | it('renders correctly', () => {
12 | const tree = renderer
13 | .create( )
14 | .toJSON();
15 | expect(tree).toMatchSnapshot();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/TermCard/TermCard.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import { StaticRouter } from 'react-router';
4 |
5 | import TermCard from './TermCard';
6 |
7 | describe('TermCard component', () => {
8 | it('renders correctly', () => {
9 | const tree = renderer
10 | .create( )
11 | .toJSON();
12 | expect(tree).toMatchSnapshot();
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/src/screens/Term/Term.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import { StaticRouter, Route } from 'react-router';
4 |
5 | import Term from './Term';
6 |
7 | jest.mock('../../utils/terms');
8 |
9 | describe('Term component', () => {
10 | it('renders correctly', () => {
11 | const tree = renderer
12 | .create( )
13 | .toJSON();
14 | expect(tree).toMatchSnapshot();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/screens/Home/__snapshots__/Home.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Home component renders correctly 1`] = `
4 |
7 |
10 | Here's the Lingo
11 |
12 |
15 | Lingo is here to help you understand the terms we all use.
16 |
17 |
22 |
23 | `;
24 |
--------------------------------------------------------------------------------
/src/screens/Home/Home.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import { StaticRouter, Route } from 'react-router';
4 |
5 | import Home from './Home';
6 |
7 | jest.mock('../../utils/terms', () => ([]));
8 |
9 | describe('Home component', () => {
10 | it('renders correctly', () => {
11 | const tree = renderer
12 | .create( )
13 | .toJSON();
14 | expect(tree).toMatchSnapshot();
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/src/components/Header/Header.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import { StaticRouter, Route } from 'react-router';
4 |
5 | import Header from './Header';
6 |
7 | jest.mock('@appearhere/react-stickynode');
8 | jest.mock('../../utils/terms');
9 |
10 | describe('Header component', () => {
11 | it('renders correctly', () => {
12 | const tree = renderer
13 | .create( )
14 | .toJSON();
15 | expect(tree).toMatchSnapshot();
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/components/Search/Search.css:
--------------------------------------------------------------------------------
1 | .root {
2 | justify-self: end;
3 | grid-area: search;
4 | overflow: hidden;
5 | min-width: 0;
6 | min-height: 0;
7 | width: 100%;
8 | }
9 |
10 | .inputRoot {
11 | width: 100%;
12 | }
13 |
14 | input[type="text"].input {
15 | padding-top: var(--size-regular);
16 | padding-bottom: var(--size-regular);
17 | background-color: var(--color-greyLightest);
18 | }
19 |
20 | input[type="text"].input:focus {
21 | outline: none;
22 | background-color: var(--color-greyLightest);
23 | border-color: var(--color-greyLighter);
24 | }
25 |
--------------------------------------------------------------------------------
/src/__snapshots__/App.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`App component renders correctly 1`] = `
4 |
7 |
10 |
13 | Here's the Lingo
14 |
15 |
18 | Lingo is here to help you understand the terms we all use.
19 |
20 |
25 |
26 |
27 | `;
28 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/ScrollToTop.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import renderer from 'react-test-renderer';
3 | import { StaticRouter } from 'react-router';
4 |
5 | import ScrollToTop from './ScrollToTop';
6 |
7 | describe('ScrollToTop component', () => {
8 | it('renders correctly', () => {
9 | const tree = renderer
10 | .create(
11 |
12 |
13 |
14 |
15 | ,
16 | )
17 | .toJSON();
18 | expect(tree).toMatchSnapshot();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/src/components/ScrollToTop/ScrollToTop.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line
2 | import React, { Component } from 'react';
3 | import PropTypes from 'prop-types';
4 | import { withRouter } from 'react-router';
5 |
6 | class ScrollToTop extends Component {
7 | static propTypes = {
8 | location: PropTypes.shape({ }),
9 | children: PropTypes.node,
10 | };
11 |
12 | componentDidUpdate(prevProps) {
13 | if (this.props.location !== prevProps.location) {
14 | window.scrollTo(0, 0);
15 | }
16 | }
17 |
18 | render() {
19 | return this.props.children;
20 | }
21 | }
22 |
23 | export default withRouter(ScrollToTop);
24 |
--------------------------------------------------------------------------------
/src/components/TermList/TermList.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import TermCard from '../TermCard/TermCard';
5 |
6 | import css from './TermList.css';
7 |
8 | const TermList = ({ terms, highlight }) => (
9 |
10 |
11 | { terms.map(term => (
12 |
18 | )) }
19 |
20 |
21 | );
22 |
23 | TermList.propTypes = {
24 | terms: PropTypes.arrayOf(PropTypes.shape({})),
25 | highlight: PropTypes.string,
26 | };
27 |
28 | TermList.defaultProps = {
29 | terms: [],
30 | };
31 |
32 | export default TermList;
33 |
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 | const jest = require('jest');
19 | const argv = process.argv.slice(2);
20 |
21 | // Watch unless on CI or in coverage mode
22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) {
23 | argv.push('--watch');
24 | }
25 |
26 |
27 | jest.run(argv);
28 |
--------------------------------------------------------------------------------
/src/screens/Home/Home.css:
--------------------------------------------------------------------------------
1 | .root {
2 | height: 100%;
3 | background-color: var(--color-greyLightest);
4 | }
5 |
6 | .iconContainer {
7 | display: flex;
8 | justify-content: center;
9 | align-items: center;
10 | font-size: var(--fontsize-large-iii);
11 | width: 100%;
12 | height: 100%;
13 | flex-direction: column;
14 | }
15 |
16 | .icon {
17 | font-size: 4rem;
18 | }
19 |
20 | .welcome {
21 | margin-top: 0;
22 | text-align: center;
23 | font-size: var(--fontsize-large-iv);
24 | margin-bottom: var(--size-regular);
25 | }
26 |
27 | .strapline {
28 | text-align: center;
29 | font-size: var(--fontsize-regular);
30 | margin-top: 0;
31 | margin-bottom: 2rem;
32 | }
33 |
34 | @media(min-width: 40rem) {
35 | .welcome {
36 | font-size: var(--fontsize-large-v);
37 | }
38 |
39 | .strapline {
40 | margin-bottom: 4rem;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/components/Highlight/Highlight.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import replace from 'string-replace-to-array';
3 | import PropTypes from 'prop-types';
4 |
5 | import css from './Highlight.css';
6 |
7 | const Highlight = ({ highlight, caseSensitive, text, ...rest }) => (
8 |
9 | { highlight
10 | ? replace(
11 | text,
12 | new RegExp(
13 | (highlight || '').replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'),
14 | caseSensitive ? 'g' : 'gi',
15 | ),
16 | (tag, index) => { tag } ,
17 | )
18 | : text }
19 |
20 | );
21 |
22 |
23 | Highlight.propTypes = {
24 | highlight: PropTypes.string,
25 | text: PropTypes.string,
26 | caseSensitive: PropTypes.bool,
27 | };
28 |
29 | Highlight.defaultProps = {
30 | caseSensitive: false,
31 | };
32 |
33 | export default Highlight;
34 |
--------------------------------------------------------------------------------
/src/components/TermCard/TermCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link } from 'react-router-dom';
4 | import cx from 'classnames';
5 |
6 | import cardCss from '@appearhere/bloom/components/Cards/Card/Card.css';
7 |
8 | import Highlight from '../Highlight/Highlight';
9 |
10 | import css from './TermCard.css';
11 |
12 | const TermCard = ({ term, highlight, className }) => (
13 |
17 |
18 |
19 | { term.department }
20 |
21 |
22 | );
23 |
24 | TermCard.propTypes = {
25 | term: PropTypes.shape({}).isRequired,
26 | highlight: PropTypes.string,
27 | className: PropTypes.string,
28 | };
29 |
30 | export default TermCard;
31 |
--------------------------------------------------------------------------------
/config/polyfills.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | if (typeof Promise === 'undefined') {
4 | // Rejection tracking prevents a common issue where React gets into an
5 | // inconsistent state due to an error, but it gets swallowed by a Promise,
6 | // and the user has no idea what causes React's erratic future behavior.
7 | require('promise/lib/rejection-tracking').enable();
8 | window.Promise = require('promise/lib/es6-extensions.js');
9 | }
10 |
11 | // fetch() polyfill for making API calls.
12 | require('whatwg-fetch');
13 |
14 | // Object.assign() is commonly used with React.
15 | // It will use the native implementation if it's present and isn't buggy.
16 | Object.assign = require('object-assign');
17 |
18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
19 | // We don't polyfill it in the browser--this is user's responsibility.
20 | if (process.env.NODE_ENV === 'test') {
21 | require('raf').polyfill(global);
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/Header/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Combined Shape
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/components/Search/__snapshots__/Search.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Search component renders correctly 1`] = `
4 |
8 |
11 |
20 |
23 |
35 |
38 |
39 |
40 |
41 | `;
42 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter as Router, Route } from 'react-router-dom';
3 | import GA from 'react-ga';
4 |
5 | import ScrollToTop from './components/ScrollToTop/ScrollToTop';
6 | import Header from './components/Header/Header';
7 |
8 | import Home from './screens/Home/Home';
9 | import Term from './screens/Term/Term';
10 |
11 | import GoogleAnalytics from './utils/GoogleAnalytics';
12 |
13 | import css from './App.css';
14 |
15 | GA.initialize(process.env.REACT_APP_GA_TRACKING_ID, {
16 | debug: process.env.NODE_ENV === 'development',
17 | });
18 |
19 | const App = () => (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | );
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/src/sampleTerms.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Aardvark",
4 | "definition": "A large, nocturnal, burrowing mammal, Orycteropus afer, of central and southern Africa, feeding on ants and termites and having a long, extensile tongue, strong claws, and long ears.",
5 | "department": "Animals",
6 | "where": "The Southern part of Africa is where the Aardvark is found in the wild. They are found South of the Sahara where it is very hot and dry. They dig tunnels and spend most of the daylight hours under the ground.",
7 | "what": "A nocturnal feeder, it subsists on ants and termites, which it will dig out of their hills using its sharp claws and powerful legs.",
8 | "when": "Based on fossils, Bryan Patterson has concluded that early relatives of the aardvark appeared in Africa around the end of the Paleocene.",
9 | "looker": "",
10 | "expected_boundary": "The aardvark is known to live in small family groups or as a solitary creature.",
11 | "hint": "If food is scarce though they will become aggressive to protect what they do find."
12 | }
13 | ]
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2013-present, Facebook, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/components/TermList/TermList.css:
--------------------------------------------------------------------------------
1 | .container {
2 | display: grid;
3 | grid-gap: var(--size-regular);
4 | min-height: 0;
5 | min-width: 0;
6 | margin-bottom: var(--size-regular);
7 | grid-template-columns: repeat(1, 1fr);
8 | max-width: 90rem;
9 | margin-left: auto;
10 | margin-right: auto;
11 | }
12 |
13 | .h2 {
14 | display: none
15 | }
16 |
17 | .item {
18 | overflow: hidden;
19 | min-width: 0;
20 | min-height: 0;
21 | }
22 |
23 | @media(min-width: 22rem) {
24 | .container {
25 | grid-template-columns: repeat(2, 1fr);
26 | }
27 | }
28 |
29 | @media(min-width: 61rem) {
30 | .container {
31 | grid-template-columns: repeat(4, 1fr);
32 | }
33 |
34 | .h2 {
35 | margin: 0;
36 | display: block;
37 | font-size: var(--fontsize-large-i);
38 | font-weight: var(--fontweight-demi);
39 | }
40 |
41 | .h2:after {
42 | margin-top: var(--size-regular);
43 | margin-bottom: var(--size-regular);
44 | background-color: var(--color-black);
45 | height: 1px;
46 | bottom: 0;
47 | content: '';
48 | display: block;
49 | left: 0;
50 | vertical-align: middle;
51 | width: 100%;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/screens/Term/__snapshots__/Term.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Term component renders correctly 1`] = `
4 |
7 |
10 |
13 |
14 |
17 |
20 |
23 | What
24 |
25 |
26 |
29 |
32 | When
33 |
34 |
35 |
38 |
41 | Where
42 |
43 |
44 |
47 |
50 | Expected Boundary
51 |
52 |
53 |
56 |
59 | Hint
60 |
61 |
62 |
63 |
64 | `;
65 |
--------------------------------------------------------------------------------
/config/eslint.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'airbnb',
3 | parser: 'babel-eslint',
4 | ecmaFeatures: {
5 | classes: true,
6 | jsx: true,
7 | },
8 | env: {
9 | jest: true,
10 | },
11 | plugins: [
12 | 'flowtype',
13 | 'jsx-a11y',
14 | 'react',
15 | ],
16 | parserOptions: {
17 | ecmaFeatures: {
18 | experimentalObjectRestSpread: true,
19 | },
20 | },
21 | globals: {
22 | document: true,
23 | window: true,
24 | navigator: true,
25 | },
26 | rules: {
27 | 'no-unused-vars': [2, {
28 | vars: 'all',
29 | args: 'after-used',
30 | varsIgnorePattern: '^_',
31 | argsIgnorePattern: '^_',
32 | }],
33 | // https://github.com/yannickcr/eslint-plugin-react/issues/621
34 | 'react/prop-types': ['warn', {
35 | ignore: ['children'],
36 | }],
37 | 'react/require-extension': 'off',
38 | 'react/jsx-filename-extension': 'off',
39 | 'import/no-extraneous-dependencies': ['error', {
40 | devDependencies: true,
41 | optionalDependencies: false,
42 | peerDependencies: false,
43 | }],
44 | 'react/no-unused-prop-types': ['error', {
45 | skipShapeProps: true,
46 | }],
47 | 'react/forbid-prop-types': 'off',
48 | 'jsx-a11y/no-static-element-interactions': 'warn',
49 | },
50 | };
51 |
--------------------------------------------------------------------------------
/src/components/TermCard/TermCard.css:
--------------------------------------------------------------------------------
1 | .root {
2 | padding: var(--size-lg-i);
3 | position: relative;
4 | background-color: var(--color-white);
5 | transition-duration: 200ms;
6 | transition-property: transform;
7 | transition-timing-function: cubic-bezier(0.65, 0.05, 0.36,1);
8 | }
9 |
10 | .root:before {
11 | content: "";
12 | display: block;
13 | padding-top: 50%;
14 | float: left;
15 | }
16 |
17 | .root > a {
18 | text-decoration: none;
19 | }
20 |
21 | .root:hover {
22 | transform: scale(0.9);
23 | }
24 |
25 | .name {
26 | font-size: var(--fontsize-regular);
27 | font-weight: var(--fontweight-demi);
28 | color: var(--color-black);
29 | }
30 |
31 | .department {
32 | position: absolute;
33 | bottom: var(--size-lg-i);
34 | font-size: var(--fontsize-small-ii);
35 | font-weight: var(--fontweight-regular);
36 | color: var(--color-black);
37 | text-transform: uppercase;
38 | }
39 |
40 | @media(min-width: 22rem) {
41 | .root:before {
42 | padding-top: 100%;
43 | }
44 | }
45 |
46 | @media(min-width: 40rem) {
47 | .name {
48 | font-size: var(--fontsize-large-ii);
49 | }
50 |
51 | .department {
52 | font-size: var(--fontsize-small-i);
53 | }
54 |
55 | .root {
56 | padding: calc(var(--size-lg-i) * 2);
57 | }
58 |
59 | .root:before {
60 | padding-top: 100%;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/Search/Search.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { withRouter } from 'react-router';
4 |
5 | import IconInput from '@appearhere/bloom/components/Form/IconInput/IconInput';
6 |
7 | import css from './Search.css';
8 |
9 | class Search extends Component {
10 | static propTypes = {
11 | match: PropTypes.shape({}),
12 | location: PropTypes.shape({}),
13 | history: PropTypes.shape({}),
14 | };
15 |
16 | handleInputChange = (e) => {
17 | const { value } = e.target;
18 |
19 | this.updateURL(value);
20 | };
21 |
22 | checkLocation = () => {
23 | const { match, history } = this.props;
24 |
25 | if (match.params.department) history.push('/');
26 | };
27 |
28 | updateURL = (query) => {
29 | const { location, history } = this.props;
30 |
31 | if (query) {
32 | history.replace(`${location.pathname}?q=${query}`);
33 | } else {
34 | history.replace(`${location.pathname}`);
35 | }
36 | };
37 |
38 | render() {
39 | const { location } = this.props;
40 | const value = location.search.substr(3);
41 |
42 | return (
43 | // eslint-disable-next-line
44 |
45 |
54 |
55 | );
56 | }
57 | }
58 |
59 | export default withRouter(Search);
60 |
--------------------------------------------------------------------------------
/src/components/Header/Header.js:
--------------------------------------------------------------------------------
1 | import React, { PureComponent } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { Link } from 'react-router-dom';
4 | import cx from 'classnames';
5 |
6 | import StickyNode from '@appearhere/bloom/components/StickyNode/StickyNode';
7 | import BtnContainer from '@appearhere/bloom/components/BtnContainer/BtnContainer';
8 | import Icon from '@appearhere/bloom/components/Icon/Icon';
9 |
10 | import Search from '../Search/Search';
11 | import { departments } from '../../utils/terms';
12 |
13 | import css from './Header.css';
14 |
15 | export default class Header extends PureComponent {
16 | static propTypes = {
17 | match: PropTypes.shape({}).isRequired,
18 | history: PropTypes.shape({}).isRequired,
19 | };
20 |
21 | handleItemClick = (e) => {
22 | const { history } = this.props;
23 | const { name } = e.target;
24 |
25 | history.push(`/${name}`);
26 | };
27 |
28 | render() {
29 | const { match } = this.props;
30 |
31 | return (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | { departments.map(department => (
40 |
48 | { department }
49 |
50 | )) }
51 |
52 |
53 |
54 |
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
23 | Lingo
24 |
25 |
26 |
27 | You need to enable JavaScript to run this app.
28 |
29 |
30 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/screens/Term/Term.css:
--------------------------------------------------------------------------------
1 | .root {
2 | background-color: var(--color-white);
3 | }
4 |
5 | .top:after {
6 | margin-top: calc(var(--size-lg-i) + var(--size-sm-ii));
7 | margin-bottom: calc(var(--size-lg-i) + var(--size-sm-ii));
8 | background-color: var(--color-greyLighter);
9 | height: 1px;
10 | bottom: 0;
11 | content: "";
12 | display: block;
13 | left: 0;
14 | vertical-align: middle;
15 | width: 100%;
16 | }
17 |
18 | .bottom {
19 | display: grid;
20 | grid-gap: calc(var(--size-lg-i) + var(--size-sm-ii));
21 | min-height: 0;
22 | min-width: 0;
23 | }
24 |
25 | .section {
26 | overflow: hidden;
27 | min-width: 0;
28 | min-height: 0;
29 | }
30 |
31 | .h1 {
32 | margin-top: 0;
33 | margin-bottom: 0;
34 | font-weight: var(--fontweight-demi);
35 | font-size: var(--fontsize-large-iv);
36 | }
37 |
38 | .h2 {
39 | text-transform: uppercase;
40 | margin-top: 0;
41 | margin-bottom: 0;
42 | font-weight: var(--fontweight-demi);
43 | font-size: var(--fontsize-large-i);
44 | white-space: nowrap;
45 | }
46 |
47 | .definition p {
48 | font-size: var(--fontsize-large-i);
49 | margin: 0;
50 | margin-top: calc(var(--size-lg-i) + var(--size-sm-ii));
51 | font-weight: var(--fontweight-demi);
52 | max-width: 43.75rem;
53 | }
54 |
55 | .body p {
56 | font-weight: var(--fontweight-regular);
57 | font-size: var(--fontsize-regular);
58 | }
59 |
60 | .linkContainer {
61 | margin-top: 1em;
62 | }
63 |
64 | .link {
65 | word-wrap: break-word;
66 | color: var(--color-gold);
67 | text-decoration: underline;
68 | }
69 |
70 | @media(min-width: 36.25rem) {
71 | .top:after {
72 | margin-top: var(--size-lg-iii);
73 | margin-bottom: var(--size-lg-iii);
74 | }
75 |
76 | .bottom {
77 | grid-template-columns: repeat(2, 1fr);
78 | grid-gap: var(--size-lg-iii);
79 | }
80 |
81 | .definition p {
82 | margin-top: var(--size-lg-iii);
83 | }
84 | }
85 |
86 | @media(min-width: 48.75rem) {
87 | .bottom {
88 | grid-template-columns: repeat(3, 1fr);
89 | }
90 | }
91 |
92 | @media(min-width: 70rem) {
93 | .root {
94 | padding-left: 12rem;
95 | padding-right: 12rem;
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/screens/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import Fuse from 'fuse.js';
4 | import qs from 'query-string';
5 | import flow from 'lodash/fp/flow';
6 | import sortBy from 'lodash/fp/sortBy';
7 | import groupBy from 'lodash/fp/groupBy';
8 | import cx from 'classnames';
9 |
10 | import ValueIconNoBull from '@appearhere/bloom/components/ValueIcons/ValueIconNoBull';
11 |
12 | import TermList from '../../components/TermList/TermList';
13 |
14 | import terms from '../../utils/terms';
15 |
16 | import sharedCss from '../../shared.css';
17 | import css from './Home.css';
18 |
19 | const fuse = new Fuse(terms, {
20 | shouldSort: true,
21 | threshold: 0.2,
22 | location: 0,
23 | distance: 100,
24 | maxPatternLength: 32,
25 | minMatchCharLength: 1,
26 | keys: ['name'],
27 | });
28 |
29 | const formatResults = flow(sortBy('name'), groupBy('department'));
30 |
31 | const Home = ({ match, location }) => {
32 | const { q: query } = qs.parse(location.search);
33 | const currentDepartment = match.params.department;
34 | const searchResults = query ? fuse.search(query) : terms;
35 | const results = currentDepartment ? formatResults(searchResults) : searchResults;
36 |
37 | return (
38 |
39 |
Here's the Lingo
40 |
41 |
42 | Lingo is here to help you understand the terms we all use.
43 |
44 |
45 | { currentDepartment
46 | ?
50 | :
54 | }
55 | { query && !results.length &&
56 |
57 | We couldn't find what you were looking for.
58 |
59 |
60 | }
61 |
62 | );
63 | };
64 |
65 |
66 | Home.propTypes = {
67 | match: PropTypes.shape({}),
68 | location: PropTypes.shape({}),
69 | };
70 |
71 | export default Home;
72 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebookincubator/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(path, needsSlash) {
15 | const hasSlash = path.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return path.substr(path, path.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${path}/`;
20 | } else {
21 | return path;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right