├── public
├── favicon.ico
├── favicon-16x16.png
├── favicon-32x32.png
├── mstile-150x150.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── browserconfig.xml
├── index.html
├── manifest.json
└── safari-pinned-tab.svg
├── .gitignore
├── static.json
├── src
├── package
│ ├── components
│ │ ├── InstructorRevenue
│ │ │ ├── utils
│ │ │ │ ├── currentMonthStartDate
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── index.test.js
│ │ │ │ ├── removeRevenueMonth
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── index.test.js
│ │ │ │ ├── prettyMonthName
│ │ │ │ │ ├── index.test.js
│ │ │ │ │ └── index.js
│ │ │ │ └── totalRevenue
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── index.test.js
│ │ │ ├── components
│ │ │ │ ├── RevenuePeriod
│ │ │ │ │ └── index.js
│ │ │ │ └── LineChart
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── Maybe
│ │ │ └── index.js
│ │ ├── Markdown
│ │ │ └── index.js
│ │ ├── LessonOverviews
│ │ │ ├── components
│ │ │ │ └── PaginatedLessonOverviews
│ │ │ │ │ ├── utils
│ │ │ │ │ └── sortLessonsByState
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── index.test.js
│ │ │ │ │ ├── components
│ │ │ │ │ └── LessonOverview
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── Error
│ │ │ └── index.js
│ │ ├── Card
│ │ │ └── index.js
│ │ ├── Open
│ │ │ └── index.js
│ │ ├── InstructorLessons
│ │ │ └── index.js
│ │ ├── Paragraph
│ │ │ └── index.js
│ │ ├── IconLabel
│ │ │ └── index.js
│ │ ├── LessonActions
│ │ │ ├── components
│ │ │ │ └── LessonAction
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── Loading
│ │ │ └── index.js
│ │ ├── RequestedLessons
│ │ │ ├── components
│ │ │ │ └── ProposeLesson
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── components
│ │ │ │ │ └── ProposeLessonForm
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── InstructorStats
│ │ │ ├── components
│ │ │ │ └── InstructorStat
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── List
│ │ │ └── index.js
│ │ ├── Image
│ │ │ └── index.js
│ │ ├── Prompt
│ │ │ └── index.js
│ │ ├── ViewportWidth
│ │ │ └── index.js
│ │ ├── LayoutColumns
│ │ │ └── index.js
│ │ ├── Anchor
│ │ │ └── index.js
│ │ ├── HeaderCard
│ │ │ └── index.js
│ │ ├── Avatar
│ │ │ └── index.js
│ │ ├── Tabs
│ │ │ └── index.js
│ │ ├── Heading
│ │ │ └── index.js
│ │ ├── ContainerWidth
│ │ │ └── index.js
│ │ ├── Button
│ │ │ └── index.js
│ │ ├── Toggle
│ │ │ └── index.js
│ │ ├── Request
│ │ │ ├── index.js
│ │ │ └── components
│ │ │ │ └── RequestBase
│ │ │ │ └── index.js
│ │ ├── LessonOverviewsByGroup
│ │ │ └── index.js
│ │ └── Icon
│ │ │ └── index.js
│ ├── utils
│ │ ├── login
│ │ │ ├── utils
│ │ │ │ ├── removeQueryString.js
│ │ │ │ └── getUrlParameter.js
│ │ │ └── index.js
│ │ ├── colors
│ │ │ └── index.js
│ │ ├── createQueryString
│ │ │ ├── index.js
│ │ │ └── index.test.js
│ │ ├── windowMock
│ │ │ └── index.js
│ │ ├── numberFormattingByType
│ │ │ └── index.js
│ │ ├── logout
│ │ │ └── index.js
│ │ ├── instructorMilestones
│ │ │ └── index.js
│ │ ├── hardCodedSizes
│ │ │ └── index.js
│ │ ├── urls
│ │ │ └── index.js
│ │ ├── createLessonsUrlWithParams
│ │ │ └── index.js
│ │ ├── colorValues
│ │ │ └── index.js
│ │ └── lessonStates
│ │ │ └── index.js
│ ├── screens
│ │ ├── InstructorsDirectory
│ │ │ ├── index.js
│ │ │ └── components
│ │ │ │ └── InstructorOverviews
│ │ │ │ ├── components
│ │ │ │ └── PaginatedInstructorOverviews
│ │ │ │ │ ├── components
│ │ │ │ │ └── InstructorOverview
│ │ │ │ │ │ ├── components
│ │ │ │ │ │ └── LessonGroupsStat
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ ├── utils
│ │ │ │ └── createInstructorsUrlWithParams.js
│ │ │ │ └── index.js
│ │ ├── InstructorDetails
│ │ │ ├── components
│ │ │ │ └── InstructorInfo
│ │ │ │ │ ├── components
│ │ │ │ │ └── InfoBit
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── InstructorDashboard
│ │ │ ├── components
│ │ │ │ ├── GetPublished
│ │ │ │ │ ├── utils
│ │ │ │ │ │ └── isStepComplete
│ │ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ │ └── index.test.js
│ │ │ │ │ ├── components
│ │ │ │ │ │ ├── Checklist
│ │ │ │ │ │ │ ├── components
│ │ │ │ │ │ │ │ └── MoreInfo
│ │ │ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ │ └── Progress
│ │ │ │ │ │ │ └── index.js
│ │ │ │ │ └── index.js
│ │ │ │ └── Help
│ │ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── LessonsDirectory
│ │ │ └── index.js
│ │ ├── NewLesson
│ │ │ └── index.js
│ │ └── LessonDetails
│ │ │ ├── components
│ │ │ ├── LessonState
│ │ │ │ └── index.js
│ │ │ └── WistiaVideo
│ │ │ │ └── index.js
│ │ │ └── index.js
│ └── index.js
├── index.js
└── App
│ ├── components
│ ├── Contributing
│ │ └── index.js
│ ├── Main
│ │ └── index.js
│ ├── ResourceDirectory
│ │ └── index.js
│ ├── Colors
│ │ └── index.js
│ ├── Icons
│ │ └── index.js
│ ├── Authentication
│ │ └── index.js
│ ├── Navigation
│ │ └── index.js
│ ├── Readme
│ │ └── index.js
│ └── Resource
│ │ ├── components
│ │ ├── Types
│ │ │ └── index.js
│ │ └── Examples
│ │ │ └── index.js
│ │ └── index.js
│ ├── utils
│ └── resourcesByType
│ │ ├── index.test.js
│ │ └── index.js
│ └── index.js
├── .travis.yml
├── README.md
├── package.json
└── CONTRIBUTING.md
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | lib/
3 | build/
4 | npm-debug.log
5 | .DS_Store
6 | .tern-port
7 | /.idea
8 |
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/mstile-150x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/mstile-150x150.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eggheadio/egghead-ui/HEAD/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/static.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": "build/",
3 | "https_only": true,
4 | "routes": {
5 | "/**": "index.html"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/currentMonthStartDate/index.js:
--------------------------------------------------------------------------------
1 | export default () => `${new Date().toISOString().slice(0,8)}01`
2 |
--------------------------------------------------------------------------------
/src/package/utils/login/utils/removeQueryString.js:
--------------------------------------------------------------------------------
1 | export default () => {
2 | history.pushState(null, '', location.href.split('?')[0])
3 | }
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import App from './App'
4 |
5 | ReactDOM.render(
6 | ,
7 | document.getElementById('root')
8 | )
9 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/removeRevenueMonth/index.js:
--------------------------------------------------------------------------------
1 | import {filter} from 'lodash'
2 |
3 | export default (revenue, month) => filter(revenue, (memo) => memo.month !== month)
--------------------------------------------------------------------------------
/src/package/utils/colors/index.js:
--------------------------------------------------------------------------------
1 | import {keys} from 'lodash'
2 | import colorValues from 'package/utils/colorValues'
3 |
4 | const colors = keys(colorValues)
5 |
6 | export default colors
7 |
--------------------------------------------------------------------------------
/src/package/utils/createQueryString/index.js:
--------------------------------------------------------------------------------
1 | import {reduce, replace, isArray} from 'lodash'
2 |
3 | export default (params) => (
4 | replace(reduce(params, (memo, value, key) => (
5 | `${memo}&${key}=${isArray(value) ? value.join(',') : value}`
6 | ), ''), '&', '?')
7 | )
8 |
--------------------------------------------------------------------------------
/public/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | #2d89ef
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | egghead-ui
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/package/utils/windowMock/index.js:
--------------------------------------------------------------------------------
1 | const windowMock = {
2 | location: {
3 | href: '',
4 | reload: () => null,
5 | },
6 | localStorage: {
7 | setItem: () => null,
8 | getItem: () => null,
9 | removeItem: () => null,
10 | },
11 | }
12 |
13 | export default windowMock
14 |
--------------------------------------------------------------------------------
/src/package/utils/numberFormattingByType/index.js:
--------------------------------------------------------------------------------
1 | import formatNumber from 'format-number'
2 |
3 | const numberFormattingByType = {
4 | general: value => formatNumber({round: 2})(value),
5 | money: value => formatNumber({round: 2, prefix: '$', padRight: 2})(value),
6 | }
7 |
8 | export default numberFormattingByType
9 |
--------------------------------------------------------------------------------
/src/package/components/Maybe/index.js:
--------------------------------------------------------------------------------
1 | import {PropTypes} from 'react'
2 |
3 | const Maybe = ({children, condition}) => condition
4 | ? children
5 | : null
6 |
7 | Maybe.propTypes = {
8 | children: PropTypes.node.isRequired,
9 | condition: PropTypes.bool.isRequired,
10 | }
11 |
12 | export default Maybe
13 |
--------------------------------------------------------------------------------
/src/App/components/Contributing/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Anchor from 'package/components/Anchor'
3 |
4 | const Contributing = () => (
5 |
6 | See CONTRIBUTING.md
7 |
8 | )
9 |
10 | export default Contributing
11 |
--------------------------------------------------------------------------------
/src/package/components/Markdown/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import ReactMarkdown from 'react-markdown'
3 |
4 | const Markdown = ({children}) => (
5 |
6 | )
7 |
8 | Markdown.propTypes = {
9 | children: PropTypes.string.isRequired,
10 | }
11 |
12 | export default Markdown
13 |
--------------------------------------------------------------------------------
/src/package/utils/logout/index.js:
--------------------------------------------------------------------------------
1 | import windowMock from 'package/utils/windowMock'
2 |
3 | const universalWindow = typeof(window) === 'undefined'
4 | ? windowMock
5 | : window
6 |
7 | const logout = () => {
8 | universalWindow.localStorage.removeItem('token')
9 | universalWindow.location.reload()
10 | }
11 |
12 | export default logout
13 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/prettyMonthName/index.test.js:
--------------------------------------------------------------------------------
1 | import prettyMonthName from '.'
2 |
3 | test('converts date strings (yyyy-mm-dd) into abbreviated month names', () => {
4 | expect(prettyMonthName('2017-01-01')).toBe('Jan')
5 | expect(prettyMonthName('2017-03-01')).toBe('Mar')
6 | expect(prettyMonthName('2025-10-01')).toBe('Oct')
7 | })
8 |
--------------------------------------------------------------------------------
/src/package/components/LessonOverviews/components/PaginatedLessonOverviews/utils/sortLessonsByState/index.js:
--------------------------------------------------------------------------------
1 | import {filter, reduce} from 'lodash'
2 | import {lessonStates} from 'package/utils/lessonStates'
3 |
4 | export default (lessons) => reduce(lessonStates, (memo, state) => ([
5 | ...memo,
6 | ...filter(lessons, lesson => lesson.state === state),
7 | ]), [])
8 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "egghead-ui",
3 | "icons": [
4 | {
5 | "src": "\/android-chrome-192x192.png",
6 | "sizes": "192x192",
7 | "type": "image\/png"
8 | },
9 | {
10 | "src": "\/android-chrome-512x512.png",
11 | "sizes": "512x512",
12 | "type": "image\/png"
13 | }
14 | ],
15 | "theme_color": "#ffffff",
16 | "display": "standalone"
17 | }
18 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/currentMonthStartDate/index.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-native-reassign */
2 |
3 | import currentMonthStart from '.'
4 |
5 | test('first day of the month', () => {
6 | const realDate = Date
7 | Date = function () {
8 | return new realDate('05/24/2013')
9 | }
10 | expect(currentMonthStart()).toBe('2013-05-01')
11 | Date = realDate
12 | })
13 |
--------------------------------------------------------------------------------
/src/package/utils/instructorMilestones/index.js:
--------------------------------------------------------------------------------
1 | export const hasUnlockedPublished = (publishedLessons) => (
2 | publishedLessons > 0
3 | )
4 |
5 | export const hasUnlockedCoursePublished = (publishedCourses) => (
6 | publishedCourses > 0
7 | )
8 |
9 | export const selfReviewThreshold = 12
10 |
11 | export const hasUnlockedSelfReview = (publishedLessons) => (
12 | publishedLessons >= selfReviewThreshold
13 | )
14 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorsDirectory/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import InstructorOverviews from './components/InstructorOverviews'
3 |
4 | const InstructorsDirectory = ({instructorsUrl}) => (
5 |
6 |
7 |
8 | )
9 |
10 | InstructorsDirectory.propTypes = {
11 | instructorsUrl: PropTypes.string.isRequired,
12 | }
13 |
14 | export default InstructorsDirectory
15 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/totalRevenue/index.js:
--------------------------------------------------------------------------------
1 | import {reduce} from 'lodash'
2 |
3 | const initialMemo = {
4 | minutes_watched: 0,
5 | revenue: 0,
6 | monthCount: 0,
7 | }
8 |
9 | export default (revenue) => reduce(revenue, (memo, monthRevenue) => ({
10 | minutes_watched: memo.minutes_watched + monthRevenue.minutes_watched,
11 | revenue: memo.revenue + monthRevenue.revenue,
12 | monthCount: memo.monthCount + 1,
13 | }), initialMemo)
14 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - node
4 | cache: yarn
5 | script: yarn verify
6 | deploy:
7 | - provider: heroku
8 | app: egghead-styleguide
9 | api_key: $AUTHOR_HEROKU_KEY
10 | - provider: npm
11 | api_key: $AUTHOR_NPM_TOKEN
12 | email: $AUTHOR_EMAIL
13 | on:
14 | repo: eggheadio/egghead-ui
15 | tags: true
16 | branch: master
17 | notifications:
18 | email:
19 | on_success: never
20 | on_failure: always
21 |
--------------------------------------------------------------------------------
/src/App/components/Main/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Heading from 'package/components/Heading'
3 |
4 | const Main = ({children, title}) => (
5 |
6 |
7 | {title}
8 |
9 | {children}
10 |
11 | )
12 |
13 | Main.propTypes = {
14 | children: PropTypes.node.isRequired,
15 | title: PropTypes.string.isRequired,
16 | }
17 |
18 | export default Main
19 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDetails/components/InstructorInfo/components/InfoBit/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Anchor from 'package/components/Anchor'
3 |
4 | export default ({label, value, href}) => (
5 |
6 |
7 | {label}
8 |
9 | {href
10 | ?
11 | {value}
12 |
13 | :
14 | {value}
15 |
16 | }
17 |
18 | )
19 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/prettyMonthName/index.js:
--------------------------------------------------------------------------------
1 | import {split} from 'lodash'
2 |
3 | const abbreviationByMonthNumber = {
4 | '01': 'Jan',
5 | '02': 'Feb',
6 | '03': 'Mar',
7 | '04': 'Apr',
8 | '05': 'May',
9 | '06': 'June',
10 | '07': 'July',
11 | '08': 'Aug',
12 | '09': 'Sept',
13 | '10': 'Oct',
14 | '11': 'Nov',
15 | '12': 'Dec',
16 | }
17 |
18 | export default (dateString) => (
19 | abbreviationByMonthNumber[split(dateString, '-')[1]]
20 | )
21 |
--------------------------------------------------------------------------------
/src/package/utils/hardCodedSizes/index.js:
--------------------------------------------------------------------------------
1 | export const xsmallContainerWidth = 320
2 | export const smallContainerWidth = 450
3 | export const mediumContainerWidth = 640
4 | export const largeContainerWidth = 992
5 | export const xlargeContainerWidth = 1300
6 | export const xxlargeContainerWidth = 1700
7 |
8 | export const containerWidths = [
9 | 'xsmall',
10 | 'small',
11 | 'medium',
12 | 'large',
13 | 'xlarge',
14 | 'xxlarge',
15 | ]
16 |
17 | export const likelyDesktopViewportWidth = 1200
18 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorsDirectory/components/InstructorOverviews/components/PaginatedInstructorOverviews/components/InstructorOverview/components/LessonGroupsStat/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Heading from 'package/components/Heading'
3 |
4 | export default ({label, count}) => (
5 | 0 ? '' : 'o-30'}`}>
6 |
7 | {label}
8 |
9 |
10 | {count}
11 |
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/src/package/utils/login/utils/getUrlParameter.js:
--------------------------------------------------------------------------------
1 | import windowMock from 'package/utils/windowMock'
2 |
3 | const universalWindow = typeof(window) === 'undefined'
4 | ? windowMock
5 | : window
6 |
7 | export default (key) => {
8 | const cleanKey = key.replace(/[[]/, '\\[').replace(/[\]]/, '\\]')
9 | const regex = new RegExp(`[\\?&]${cleanKey}=([^]*)`)
10 | const results = regex.exec(universalWindow.location.search)
11 | return results === null
12 | ? ''
13 | : decodeURIComponent(results[1].replace(/\+/g, ' '))
14 | }
15 |
--------------------------------------------------------------------------------
/src/package/components/Error/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Icon from 'package/components/Icon'
3 |
4 | const Error = ({children}) => (
5 |
6 |
7 |
12 |
13 |
14 | {children}
15 |
16 |
17 | )
18 |
19 | Error.propTypes = {
20 | children: PropTypes.string.isRequired,
21 | }
22 |
23 | export default Error
24 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorsDirectory/components/InstructorOverviews/utils/createInstructorsUrlWithParams.js:
--------------------------------------------------------------------------------
1 | import createQueryString from 'package/utils/createQueryString'
2 |
3 | const createLessonsUrlWithParams = ({
4 | pageSize = 10,
5 | page = 1,
6 | instructorsUrl,
7 | }) => {
8 |
9 | const params = {
10 | 'page': page,
11 | 'per_page': pageSize,
12 | }
13 | const queryString = createQueryString(params)
14 |
15 | return `${instructorsUrl}${queryString}`
16 | }
17 |
18 | export default createLessonsUrlWithParams
19 |
--------------------------------------------------------------------------------
/src/package/utils/createQueryString/index.test.js:
--------------------------------------------------------------------------------
1 | import createQueryString from '.'
2 |
3 | test('adds question mark but no ampersands for only one param', () => (
4 | expect(createQueryString({answer: 42})).toBe('?answer=42')
5 | ))
6 |
7 | test('adds ampersands between multiple params', () => (
8 | expect(createQueryString({answer: 42, lucky: 7})).toBe('?answer=42&lucky=7')
9 | ))
10 |
11 | test('creates comma-separated lists for arrays', () => (
12 | expect(createQueryString({alphabet: ['a', 'b', 'c']})).toBe('?alphabet=a,b,c')
13 | ))
14 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/GetPublished/utils/isStepComplete/index.js:
--------------------------------------------------------------------------------
1 | import {includes, indexOf, slice, some} from 'lodash'
2 | import {lessonStates} from 'package/utils/lessonStates'
3 |
4 | export default (instructorLessonStates, minimumLessonState) => {
5 | const qualifyingLessonStates = slice(
6 | lessonStates,
7 | indexOf(lessonStates, minimumLessonState)
8 | )
9 | return some(
10 | qualifyingLessonStates,
11 | qualifyingLessonState => includes(instructorLessonStates, qualifyingLessonState)
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/package/screens/LessonsDirectory/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Heading from 'package/components/Heading'
3 | import LessonOverviewsByGroup from 'package/components/LessonOverviewsByGroup'
4 |
5 | const LessonsDirectory = ({lessonsUrl}) => (
6 |
7 |
8 | All Lessons
9 |
10 |
11 |
12 | )
13 |
14 | LessonsDirectory.propTypes = {
15 | lessonsUrl: PropTypes.string.isRequired,
16 | }
17 |
18 | export default LessonsDirectory
19 |
--------------------------------------------------------------------------------
/src/App/components/ResourceDirectory/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import {Link} from 'react-router-dom'
4 |
5 | const ResourceDirectory = ({resources}) => (
6 |
7 | {map(resources.items, (value, key) => (
8 |
13 | {key}
14 |
15 | ))}
16 |
17 | )
18 |
19 | export default ResourceDirectory
20 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/components/RevenuePeriod/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import numberFormattingByType from 'package/utils/numberFormattingByType'
3 |
4 | export default ({title, revenue, subscriberMinutes}) => (
5 |
6 |
7 | {title}
8 |
9 |
10 | {numberFormattingByType.money(revenue)}
11 |
12 |
13 | {`${numberFormattingByType.general(subscriberMinutes)} minutes`}
14 |
15 |
16 | )
17 |
--------------------------------------------------------------------------------
/src/App/utils/resourcesByType/index.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOMServer from 'react-dom/server'
3 | import {map} from 'lodash'
4 | import resourcesByType from '.'
5 |
6 | const AllResources = () => (
7 |
8 | {map(resourcesByType, (value, key) => {
9 | const resources = resourcesByType[key]
10 | return map(resources.items, (resource) => (
11 | resource.createExamples()
12 | ))
13 | })}
14 |
15 | )
16 |
17 | it('renders on a server without crashing', () => {
18 | ReactDOMServer.renderToString( )
19 | })
20 |
--------------------------------------------------------------------------------
/src/package/screens/NewLesson/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import RequestedLessons from 'package/components/RequestedLessons'
3 |
4 | const NewLesson = ({
5 | instructor,
6 | lessonsUrl,
7 | technologiesUrl,
8 | }) => (
9 |
10 |
15 |
16 | )
17 |
18 | NewLesson.propTypes = {
19 | instructor: PropTypes.object.isRequired,
20 | lessonsUrl: PropTypes.string.isRequired,
21 | }
22 |
23 | export default NewLesson
24 |
--------------------------------------------------------------------------------
/src/App/components/Colors/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import colors from 'package/utils/colors'
4 |
5 | const Colors = () => (
6 |
7 | {map(colors, (color) => (
8 |
15 |
16 |
17 | {color}
18 |
19 |
20 | ))}
21 |
22 | )
23 |
24 | export default Colors
25 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/totalRevenue/index.test.js:
--------------------------------------------------------------------------------
1 | import totalRevenue from '.'
2 |
3 | const revenueFixture = [
4 | {
5 | month: '2016-02-01',
6 | minutes_watched: 5,
7 | revenue: 2,
8 | },
9 | {
10 | month: '2016-03-01',
11 | minutes_watched: 15,
12 | revenue: 3,
13 | },
14 | {
15 | month: '2016-04-01',
16 | minutes_watched: 30,
17 | revenue: 5,
18 | },
19 | ]
20 |
21 | test('total revenue', () => (
22 | expect(totalRevenue(revenueFixture)).toEqual({
23 | minutes_watched: 50,
24 | revenue: 10,
25 | monthCount: 3,
26 | })
27 | ))
28 |
--------------------------------------------------------------------------------
/src/package/components/Card/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {keys, first} from 'lodash'
3 |
4 | const classNameByLevel = {
5 | 1: 'br2 shadow-1',
6 | 2: 'br3 shadow-2',
7 | }
8 |
9 | export const levels = keys(classNameByLevel)
10 |
11 | const Card = ({children, level}) => (
12 |
15 | )
16 |
17 | Card.propTypes = {
18 | children: PropTypes.node.isRequired,
19 | level: PropTypes.oneOf(levels),
20 | }
21 |
22 | Card.defaultProps = {
23 | level: first(levels),
24 | }
25 |
26 | export default Card
27 |
--------------------------------------------------------------------------------
/src/package/components/Open/index.js:
--------------------------------------------------------------------------------
1 | import {PropTypes, Component} from 'react'
2 |
3 | export default class Open extends Component {
4 |
5 | state = {
6 | isOpen: false,
7 | }
8 |
9 | handleOpenToggleClick = () => {
10 | const {isOpen} = this.state
11 | this.setState({
12 | isOpen: !isOpen,
13 | })
14 | }
15 |
16 | render() {
17 | const {isOpen} = this.state
18 | const {children} = this.props
19 | return children({
20 | isOpen,
21 | handleOpenToggleClick: this.handleOpenToggleClick,
22 | })
23 | }
24 | }
25 |
26 | Open.propTypes = {
27 | children: PropTypes.func.isRequired,
28 | }
29 |
--------------------------------------------------------------------------------
/src/package/utils/urls/index.js:
--------------------------------------------------------------------------------
1 | export const guideUrl = 'https://instructor.egghead.io/guide'
2 |
3 | export const chatUrl = 'https://eggheadio.slack.com/messages/egghead-instructors/'
4 | export const instructorsChatUrl = 'https://eggheadio.slack.com/messages/egghead-instructors/'
5 |
6 | export const chatInfoUrl = 'https://instructor.egghead.io/guide/01-invited/invited.html'
7 | export const roughDraftInfoUrl = 'https://instructor.egghead.io/guide/01-invited/first-lesson.html'
8 | export const gearSetupInfoUrl = 'https://instructor.egghead.io/guide/02-creating-lessons/recording-gear.html'
9 |
10 | export const publicLessonsUrl = 'https://egghead.io/lessons'
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # egghead-ui
2 |
3 | egghead UI pieces as a **package** and **app**
4 | - **package**: [npmjs.com/package/egghead-ui](https://npmjs.com/package/egghead-ui) (exports egghead UI resources for use in other egghead projects via npm)
5 | - **app**: [styleguide.egghead.io](https://styleguide.egghead.io) (documentation and sandboxed development of the **package** resources)
6 |
7 | ## Usage
8 |
9 | ```
10 | $ yarn add egghead-ui
11 | ```
12 |
13 | ```
14 | import {resource} from 'egghead-ui'
15 | ```
16 |
17 | View [the **app**](https://styleguide.egghead.io) for full documentation and examples
18 |
19 | ## Contributing
20 |
21 | See [CONTRIBUTING.md](CONTRIBUTING.md)
22 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/GetPublished/components/Checklist/components/MoreInfo/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Maybe from 'package/components/Maybe'
3 | import Icon from 'package/components/Icon'
4 | import Anchor from 'package/components/Anchor'
5 |
6 | const MoreInfo = ({url}) => (
7 |
8 |
12 |
16 |
17 |
18 | )
19 |
20 | MoreInfo.propTypes = {
21 | url: PropTypes.string.isRequired,
22 | }
23 |
24 | export default MoreInfo
25 |
--------------------------------------------------------------------------------
/src/package/components/InstructorLessons/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Heading from 'package/components/Heading'
3 | import LessonOverviewsByGroup from 'package/components/LessonOverviewsByGroup'
4 |
5 | const InstructorLessons = ({instructor}) => instructor
6 | ?
7 |
8 | {`${instructor.first_name}'s Lessons`}
9 |
10 |
14 |
15 | : null
16 |
17 | InstructorLessons.propTypes = {
18 | instructor: PropTypes.object,
19 | }
20 |
21 | export default InstructorLessons
22 |
--------------------------------------------------------------------------------
/src/App/components/Icons/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import Icon, { types } from 'package/components/Icon'
4 |
5 | const Icons = () => (
6 |
7 | {map(types, (type) => (
8 |
12 | {
13 |
14 |
15 | {type}
16 |
17 | }
18 |
19 | ))}
20 |
21 | )
22 |
23 | export default Icons
24 |
25 |
--------------------------------------------------------------------------------
/src/package/components/Paragraph/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {keys, first} from 'lodash'
3 |
4 | const sharedClassName = 'mt0'
5 |
6 | const classNameByType = {
7 | medium: 'f5',
8 | small: 'f6',
9 | }
10 |
11 | export const types = keys(classNameByType)
12 |
13 | const Paragraph = ({
14 | children,
15 | type,
16 | }) => (
17 |
18 | {children}
19 |
20 | )
21 |
22 | Paragraph.propTypes = {
23 | children: PropTypes.string.isRequired,
24 | type: PropTypes.oneOf(types),
25 | }
26 |
27 | Paragraph.defaultProps = {
28 | type: first(types),
29 | }
30 |
31 | export default Paragraph
32 |
--------------------------------------------------------------------------------
/src/package/utils/createLessonsUrlWithParams/index.js:
--------------------------------------------------------------------------------
1 | import createQueryString from 'package/utils/createQueryString'
2 |
3 | const createLessonsUrlWithParams = ({
4 | states,
5 | pageSize = 10,
6 | page = 1,
7 | lessonsUrl,
8 | includeLessonsInCourses = true,
9 | }) => {
10 |
11 | const params = {
12 | 'page': page,
13 | 'per_page': pageSize,
14 | ...(states
15 | ? {state: states}
16 | : {}
17 | ),
18 | 'sort': ['state', 'row_order'],
19 | 'without_course': !includeLessonsInCourses,
20 | }
21 | const queryString = createQueryString(params)
22 |
23 | return `${lessonsUrl}${queryString}`
24 | }
25 |
26 | export default createLessonsUrlWithParams
27 |
--------------------------------------------------------------------------------
/src/package/screens/LessonDetails/components/LessonState/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Paragraph from 'package/components/Paragraph'
3 | import {detailsByLessonState} from 'package/utils/lessonStates'
4 |
5 | export default ({lesson}) => {
6 | const statusColor = detailsByLessonState[lesson.state].color
7 | return (
8 |
9 |
13 | {detailsByLessonState[lesson.state].title || lesson.state}
14 |
15 |
16 | {detailsByLessonState[lesson.state].description}
17 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/src/package/components/LessonOverviews/components/PaginatedLessonOverviews/utils/sortLessonsByState/index.test.js:
--------------------------------------------------------------------------------
1 | import sortLessonsByState from '.'
2 |
3 | test('sorts lessons by the lesson state flow order', () => (
4 | expect(sortLessonsByState([
5 | {state: 'claimed'},
6 | {state: 'published'},
7 | {state: 'retired'},
8 | {state: 'published'},
9 | {state: 'published'},
10 | {state: 'accepted'},
11 | {state: 'approved'},
12 | {state: 'published'},
13 | ])).toEqual([
14 | {state: 'accepted'},
15 | {state: 'claimed'},
16 | {state: 'approved'},
17 | {state: 'published'},
18 | {state: 'published'},
19 | {state: 'published'},
20 | {state: 'published'},
21 | {state: 'retired'},
22 | ])
23 | ))
24 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/utils/removeRevenueMonth/index.test.js:
--------------------------------------------------------------------------------
1 | import removeRevenueMonth from '.'
2 |
3 | const revenueFixture = [
4 | {
5 | month: '2016-02-01',
6 | minutes_watched: 5,
7 | revenue: 2,
8 | },
9 | {
10 | month: '2016-03-01',
11 | minutes_watched: 15,
12 | revenue: 3,
13 | },
14 | {
15 | month: '2016-04-01',
16 | minutes_watched: 30,
17 | revenue: 5,
18 | },
19 | ]
20 |
21 | test('revenue without a month', () => (
22 | expect(removeRevenueMonth(revenueFixture, '2016-04-01')).toEqual([
23 | {
24 | month: '2016-02-01',
25 | minutes_watched: 5,
26 | revenue: 2,
27 | },
28 | {
29 | month: '2016-03-01',
30 | minutes_watched: 15,
31 | revenue: 3,
32 | },
33 | ])
34 | ))
35 |
--------------------------------------------------------------------------------
/src/package/components/IconLabel/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import colors from 'package/utils/colors'
3 | import Icon, {types as iconTypes} from 'package/components/Icon'
4 |
5 | const IconLabel = ({
6 | iconType,
7 | labelText,
8 | color,
9 | }) => (
10 |
11 |
12 |
16 |
17 |
18 | {labelText}
19 |
20 |
21 | )
22 |
23 | IconLabel.propTypes = {
24 | iconType: PropTypes.oneOf(iconTypes).isRequired,
25 | labelText: PropTypes.string.isRequired,
26 | color: PropTypes.oneOf(colors),
27 | }
28 |
29 | IconLabel.defaultProps = {
30 | color: 'dark-gray',
31 | }
32 |
33 | export default IconLabel
34 |
--------------------------------------------------------------------------------
/src/package/utils/login/index.js:
--------------------------------------------------------------------------------
1 | import jwt from 'jwt-simple'
2 | import windowMock from 'package/utils/windowMock'
3 | import removeQueryString from './utils/removeQueryString'
4 | import getUrlParameter from './utils/getUrlParameter'
5 |
6 | const universalWindow = typeof(window) === 'undefined'
7 | ? windowMock
8 | : window
9 |
10 | const decodeToken = (token) => jwt.decode(token, null, true)
11 |
12 | const login = () => {
13 | if (universalWindow.localStorage.getItem('token')) {
14 | const token = universalWindow.localStorage.getItem('token')
15 | return decodeToken(token)
16 | }
17 | if (getUrlParameter('jwt')) {
18 | const token = getUrlParameter('jwt')
19 | universalWindow.localStorage.setItem('token', token)
20 | removeQueryString()
21 | return decodeToken(token)
22 | }
23 | }
24 |
25 | export default login
26 |
--------------------------------------------------------------------------------
/src/package/components/LessonActions/components/LessonAction/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ContainerWidth from 'package/components/ContainerWidth'
3 | import Icon from 'package/components/Icon'
4 |
5 | export default ({
6 | actionText,
7 | iconType,
8 | color,
9 | url,
10 | onClick,
11 | }) => (
12 |
13 | {(containerWidth) => (
14 |
19 |
20 |
24 |
25 |
26 | {actionText}
27 |
28 |
29 | )}
30 |
31 | )
32 |
--------------------------------------------------------------------------------
/src/package/components/Loading/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import styled, {keyframes} from 'styled-components'
3 | import Icon from 'package/components/Icon'
4 |
5 | const rotate360 = keyframes`
6 | from {
7 | transform: rotate(0deg);
8 | }
9 | to {
10 | transform: rotate(360deg);
11 | }
12 | `
13 |
14 | const StyledDiv = styled.div`
15 | animation: ${rotate360} 0.7s linear infinite;
16 | `
17 |
18 | const Loading = () => (
19 |
25 |
26 |
27 |
31 |
32 |
33 |
34 | Loading...
35 |
36 |
37 | )
38 |
39 | export default Loading
40 |
--------------------------------------------------------------------------------
/src/package/components/RequestedLessons/components/ProposeLesson/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Open from 'package/components/Open'
3 | import IconLabel from 'package/components/IconLabel'
4 | import ProposeLessonForm from './components/ProposeLessonForm'
5 |
6 | export default ({instructor, lessonsUrl, technologiesUrl}) => (
7 |
8 | {({isOpen, handleOpenToggleClick}) => isOpen
9 | ?
14 | :
18 |
23 |
24 | }
25 |
26 | )
27 |
--------------------------------------------------------------------------------
/src/package/components/InstructorStats/components/InstructorStat/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import Anchor from 'package/components/Anchor'
4 | import Image from 'package/components/Image'
5 |
6 | export default ({count, label, graphics}) => (
7 |
8 |
9 |
10 | {count}
11 |
12 |
13 | {label}
14 |
15 |
16 |
17 | {map(graphics, graphic => (
18 |
22 |
27 |
28 | ))}
29 |
30 |
31 | )
32 |
--------------------------------------------------------------------------------
/src/package/components/List/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {map, keys} from 'lodash'
3 |
4 | const paddingClassNameBySize = {
5 | small: 'pa3',
6 | medium: 'pa4',
7 | large: 'pa5',
8 | }
9 |
10 | export const sizes = keys(paddingClassNameBySize)
11 |
12 | const List = ({
13 | items,
14 | size = 'medium',
15 | overDark = false,
16 | }) => (
17 |
18 | {map(items, (item, index) => (
19 |
31 | {item}
32 |
33 | ))}
34 |
35 | )
36 |
37 | List.propTypes = {
38 | items: PropTypes.arrayOf(PropTypes.node).isRequired,
39 | size: PropTypes.oneOf(sizes),
40 | overDark: PropTypes.bool,
41 | }
42 |
43 | export default List
44 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDetails/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import LayoutColumns from 'package/components/LayoutColumns'
3 | import InstructorStats from 'package/components/InstructorStats'
4 | import InstructorLessons from 'package/components/InstructorLessons'
5 | import InstructorRevenue from 'package/components/InstructorRevenue'
6 | import InstructorInfo from './components/InstructorInfo'
7 |
8 | const InstructorDetails = ({instructor}) => (
9 |
10 |
13 |
14 |
,
15 | ,
16 | ]}
17 | relativeSizes={[1, 2]}
18 | />
19 | ,
22 | ,
23 | ]}
24 | relativeSizes={[1, 2]}
25 | />
26 |
27 | )
28 |
29 | InstructorDetails.propTypes = {
30 | instructor: PropTypes.object,
31 | }
32 |
33 | export default InstructorDetails
34 |
--------------------------------------------------------------------------------
/src/package/components/Image/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes, Component} from 'react'
2 |
3 | export default class Image extends Component {
4 |
5 | state = {
6 | hasLoaded: false,
7 | }
8 |
9 | handleLoad = () => {
10 | this.setState({hasLoaded: true});
11 | }
12 |
13 | render() {
14 | const {hasLoaded} = this.state
15 | const {src, alt, className} = this.props
16 |
17 | if(hasLoaded) {
18 | return (
19 |
24 | )
25 | }
26 |
27 | return (
28 |
29 |
37 |
38 | )
39 | }
40 | }
41 |
42 | Image.propTypes = {
43 | src: PropTypes.string.isRequired,
44 | alt: PropTypes.string.isRequired,
45 | className: PropTypes.string.isRequired,
46 | }
47 |
48 | Image.defaultProps = {
49 | className: '',
50 | }
51 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/GetPublished/components/Progress/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 |
3 | const barHeight = 7
4 |
5 | const Progress = ({total, complete}) => (
6 |
7 |
8 |
9 |
10 | {complete}
11 |
12 |
13 | /
14 |
15 |
16 | {total}
17 |
18 |
19 | completed
20 |
21 |
22 |
23 |
39 |
40 | )
41 |
42 | Progress.propTypes = {
43 | total: PropTypes.number.isRequired,
44 | complete: PropTypes.number.isRequired,
45 | }
46 |
47 | export default Progress
48 |
--------------------------------------------------------------------------------
/src/package/screens/LessonDetails/components/WistiaVideo/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import Paragraph from 'package/components/Paragraph'
3 |
4 | export default class extends Component {
5 |
6 | shouldComponentUpdate(nextProps) {
7 | return this.props.wistiaId !== nextProps.wistiaId
8 | }
9 |
10 | render() {
11 | const {wistiaId} = this.props
12 | return wistiaId
13 | ?
30 | :
31 | There is no video to show - waiting for one to be uploaded.
32 |
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/App/components/Authentication/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import login from 'package/utils/login'
3 | import logout from 'package/utils/logout'
4 | import Heading from 'package/components/Heading'
5 | import Anchor from 'package/components/Anchor'
6 | import Request from 'package/components/Request'
7 |
8 | const Authentication = ({children, loginUrl, userPermissionProperty}) => {
9 |
10 | const decodedToken = login()
11 |
12 | if(!decodedToken) {
13 | return (
14 |
15 |
16 | You need to log in to view this
17 |
18 |
19 | Sign in via egghead
20 |
21 |
22 | )
23 | }
24 |
25 | if(decodedToken && !decodedToken.user_url) {
26 | logout()
27 | }
28 |
29 | return (
30 |
31 | {({data}) => children({data})}
32 |
33 | )
34 | }
35 |
36 | Authentication.propTypes = {
37 | children: PropTypes.func.isRequired,
38 | loginUrl: PropTypes.string.isRequired,
39 | }
40 |
41 | export default Authentication
42 |
--------------------------------------------------------------------------------
/src/package/components/Prompt/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {Link} from 'react-router-dom'
3 | import {startsWith} from 'lodash'
4 | import Button from 'package/components/Button'
5 | import Paragraph from 'package/components/Paragraph'
6 |
7 | const Prompt = ({
8 | description,
9 | actionText,
10 | action,
11 | }) => (
12 |
13 |
14 | {description}
15 |
16 |
36 |
37 | )
38 |
39 | Prompt.propTypes = {
40 | description: PropTypes.string.isRequired,
41 | actionText: PropTypes.string.isRequired,
42 | action: PropTypes.string.isRequired,
43 | }
44 |
45 | export default Prompt
46 |
--------------------------------------------------------------------------------
/src/package/components/ViewportWidth/index.js:
--------------------------------------------------------------------------------
1 | import {PropTypes, Component} from 'react'
2 | import {likelyDesktopViewportWidth} from 'package/utils/hardCodedSizes'
3 |
4 | class ViewportWidth extends Component {
5 |
6 | state = {
7 | isLikelyDesktop: false,
8 | }
9 |
10 | componentDidMount = () => {
11 | this.handleWidthChange()
12 | window.onresize = this.handleWidthChange
13 | }
14 |
15 | componentWillUnmount = () => {
16 | window.onresize = null
17 | }
18 |
19 | handleWidthChange = () => {
20 | const {onWidthChange} = this.props
21 | if(onWidthChange) {
22 | onWidthChange()
23 | }
24 | if(window.innerWidth >= likelyDesktopViewportWidth) {
25 | this.setState({
26 | isLikelyDesktop: true,
27 | })
28 | }
29 | else {
30 | this.setState({
31 | isLikelyDesktop: false,
32 | })
33 | }
34 | }
35 |
36 | render() {
37 | const {isLikelyDesktop} = this.state
38 | const {children} = this.props
39 | return children(isLikelyDesktop)
40 | }
41 | }
42 |
43 | ViewportWidth.propTypes = {
44 | children: PropTypes.func.isRequired,
45 | onWidthChange: PropTypes.func,
46 | }
47 |
48 | export default ViewportWidth
49 |
--------------------------------------------------------------------------------
/src/package/components/LayoutColumns/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {map} from 'lodash'
3 | import {xsmallContainerWidth} from 'package/utils/hardCodedSizes'
4 | import ContainerWidth from 'package/components/ContainerWidth'
5 |
6 | const LayoutColumns = ({items, relativeSizes}) => (
7 |
8 | {(containerWidth) => (
9 |
10 | {map(items, (item, index) => (
11 |
26 | {item}
27 |
28 | ))}
29 |
30 | )}
31 |
32 | )
33 |
34 | LayoutColumns.propTypes = {
35 | items: PropTypes.arrayOf(PropTypes.node).isRequired,
36 | relativeSizes: PropTypes.arrayOf(PropTypes.number),
37 | }
38 |
39 | export default LayoutColumns
40 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/GetPublished/utils/isStepComplete/index.test.js:
--------------------------------------------------------------------------------
1 | import isStepComplete from '.'
2 |
3 | test('all steps are incomplete when instructor has no lessons', () => {
4 | const instructorLessonStates = []
5 | expect(isStepComplete(instructorLessonStates, 'submitted')).toBe(false)
6 | expect(isStepComplete(instructorLessonStates, 'approved')).toBe(false)
7 | })
8 |
9 | test('all steps are complete when instructor has at least one published lesson', () => {
10 | const instructorLessonStates = [
11 | 'submitted',
12 | 'approved',
13 | 'published',
14 | ]
15 | expect(isStepComplete(instructorLessonStates, 'submitted')).toBe(true)
16 | expect(isStepComplete(instructorLessonStates, 'updated')).toBe(true)
17 | expect(isStepComplete(instructorLessonStates, 'approved')).toBe(true)
18 | expect(isStepComplete(instructorLessonStates, 'published')).toBe(true)
19 | })
20 |
21 | test('steps that are less than or equal to a completed step are also complete', () => {
22 | const instructorLessonStates = [
23 | 'approved',
24 | ]
25 | expect(isStepComplete(instructorLessonStates, 'submitted')).toBe(true)
26 | expect(isStepComplete(instructorLessonStates, 'approved')).toBe(true)
27 | expect(isStepComplete(instructorLessonStates, 'published')).toBe(false)
28 | })
29 |
--------------------------------------------------------------------------------
/src/App/components/Navigation/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {NavLink} from 'react-router-dom'
3 | import windowMock from 'package/utils/windowMock'
4 | import Button from 'package/components/Button'
5 | import logout from 'package/utils/logout'
6 |
7 | const universalWindow = typeof(window) === 'undefined'
8 | ? windowMock
9 | : window
10 |
11 | const Navigation = ({items}) => (
12 |
13 |
14 | {items.map((item) => (
15 |
22 | {item.label}
23 |
24 | ))}
25 |
26 |
27 | {universalWindow.localStorage.getItem('token')
28 | ?
33 | Logout
34 |
35 | : null
36 | }
37 |
38 |
39 | )
40 |
41 | Navigation.propTypes = {
42 | items: PropTypes.arrayOf(React.PropTypes.shape({
43 | label: PropTypes.string.isRequired,
44 | path: PropTypes.string.isRequired,
45 | })).isRequired,
46 | }
47 |
48 | export default Navigation
49 |
--------------------------------------------------------------------------------
/src/package/components/Anchor/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {first} from 'lodash'
3 | import colors from 'package/utils/colors'
4 | import Icon from 'package/components/Icon'
5 |
6 | export const types = ['inline', 'prominent']
7 |
8 | const Anchor = ({
9 | children,
10 | url,
11 | isSeparateTab,
12 | type,
13 | color,
14 | }) => {
15 |
16 | const classNameByType = {
17 | inline: color,
18 | prominent: `${color} ttu no-underline flex items-center`,
19 | }
20 |
21 | const prefixByType = {
22 | inline: null,
23 | prominent: (
24 |
25 |
30 |
31 | ),
32 | }
33 |
34 | return (
35 |
40 | {prefixByType[type]}{children}
41 |
42 | )
43 | }
44 |
45 | Anchor.propTypes = {
46 | children: PropTypes.node.isRequired,
47 | url: PropTypes.string.isRequired,
48 | isSeparateTab: PropTypes.bool,
49 | type: PropTypes.oneOf(types),
50 | color: PropTypes.oneOf(colors).isRequired,
51 | }
52 |
53 | Anchor.defaultProps = {
54 | isSeparateTab: false,
55 | type: first(types),
56 | color: 'blue',
57 | }
58 |
59 | export default Anchor
60 |
--------------------------------------------------------------------------------
/src/package/components/HeaderCard/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Maybe from 'package/components/Maybe'
3 | import Heading from 'package/components/Heading'
4 | import Paragraph from 'package/components/Paragraph'
5 | import Card from 'package/components/Card'
6 |
7 | const HeaderCard = ({
8 | children,
9 | title,
10 | description,
11 | intro,
12 | subtle,
13 | }) => (
14 |
15 |
16 |
17 | {title}
18 |
19 | {description
20 | ?
21 | {description}
22 |
23 | : null
24 | }
25 |
26 |
27 | {intro}
28 |
29 |
30 |
31 | {subtle
32 | ?
33 | {children}
34 |
35 | :
36 | {children}
37 |
38 | }
39 |
40 | )
41 |
42 | HeaderCard.propTypes = {
43 | children: PropTypes.node.isRequired,
44 | title: PropTypes.string.isRequired,
45 | description: PropTypes.string,
46 | intro: PropTypes.node,
47 | subtle: PropTypes.bool,
48 | }
49 |
50 | HeaderCard.defaultProps = {
51 | description: null,
52 | intro: null,
53 | subtle: false,
54 | }
55 |
56 | export default HeaderCard
57 |
--------------------------------------------------------------------------------
/src/App/components/Readme/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Heading from 'package/components/Heading'
3 | import Markdown from 'package/components/Markdown'
4 | import Paragraph from 'package/components/Paragraph'
5 |
6 | const Readme = () => (
7 |
8 |
9 |
10 | 1) Install
11 |
12 |
13 | {"`$ yarn add egghead-ui`"}
14 |
15 |
16 |
17 | 2) Import
18 |
19 |
20 | {"`import {resource} from 'egghead-ui'`"}
21 |
22 |
23 |
24 | 3) Upgrade
25 |
26 |
27 | {"`$ yarn upgrade egghead-ui`"}
28 |
29 |
30 | When new versions are released
31 |
32 |
33 |
34 |
35 | Notes
36 |
37 |
38 | Components that request data require you to log in with an egghead account (local storage needs a "token" key with a jwt for endpoint use). These components also conditionally display parts of their UI based on what is available to the currently logged in user. This means that when viewing examples sometimes nothing will be rendered (for example, if the passed in instructor or lesson doesn't have certain data); when this happens, keep tapping "Randomize" until you get a set of data that can show the component.
39 |
40 |
41 |
42 |
43 | )
44 |
45 | export default Readme
46 |
--------------------------------------------------------------------------------
/src/package/components/Avatar/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes, Component} from 'react'
2 | import {keys} from 'lodash'
3 |
4 | export const classNameBySizes = {
5 | 1: 'w1 h1',
6 | 2: 'w2 h2',
7 | 3: 'w3 h3',
8 | 4: 'w4 h4',
9 | }
10 |
11 | export const sizes = keys(classNameBySizes)
12 |
13 | export default class Avatar extends Component {
14 |
15 | state = {
16 | hasLoaded: false,
17 | }
18 |
19 | handleLoad = () => {
20 | this.setState({hasLoaded: true});
21 | }
22 |
23 | render() {
24 | const {hasLoaded} = this.state
25 | const {name, url, size} = this.props
26 | const alt = `Avatar for ${name}`
27 | const containerClassName = `bg-gray-secondary dib br-100 ${classNameBySizes[size]}`
28 |
29 | if(hasLoaded) {
30 | return (
31 |
39 | )
40 | }
41 |
42 | return (
43 |
44 |
52 |
53 | )
54 | }
55 | }
56 |
57 | Avatar.propTypes = {
58 | name: PropTypes.string.isRequired,
59 | url: PropTypes.string.isRequired,
60 | size: PropTypes.oneOf(sizes).isRequired,
61 | }
62 |
63 | Avatar.defaultProps = {
64 | size: '3',
65 | }
66 |
--------------------------------------------------------------------------------
/src/App/components/Resource/components/Types/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {map, isArray} from 'lodash'
3 | import {Link} from 'react-router-dom'
4 | import colors from 'package/utils/colors'
5 | import Heading from 'package/components/Heading'
6 |
7 | const Arguments = ({title, types}) => (
8 |
9 |
10 | {title}
11 |
12 |
13 | {map(types, (value, key) => (
14 |
22 |
23 | {value === colors
24 | ?
28 | color
29 |
30 | : key
31 | }
32 |
33 |
34 | {isArray(value)
35 | ?
36 | {map(value, x => (
37 |
38 | '{x}'
39 |
40 | ))}
41 |
42 | : value
43 | }
44 |
45 |
46 | ))}
47 |
48 |
49 | )
50 |
51 | Arguments.propTypes = {
52 | title: PropTypes.string.isRequired,
53 | types: PropTypes.object.isRequired,
54 | }
55 |
56 | export default Arguments
57 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDetails/components/InstructorInfo/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map, compact} from 'lodash'
3 | import List from 'package/components/List'
4 | import HeaderCard from 'package/components/HeaderCard'
5 | import Avatar from 'package/components/Avatar'
6 | import InfoBit from './components/InfoBit'
7 |
8 | export default ({instructor}) => {
9 |
10 | const items = compact([
11 | ,
18 | instructor.bio || instructor.bio_short
19 | ?
23 | : null,
24 | instructor.slack_id
25 | ?
30 | : null,
31 | instructor.website
32 | ?
37 | : null,
38 | instructor.twitter
39 | ?
44 | : null,
45 | ])
46 |
47 | return (
48 |
49 | item)} />
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/src/package/components/RequestedLessons/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import Paragraph from 'package/components/Paragraph'
3 | import Heading from 'package/components/Heading'
4 | import Card from 'package/components/Card'
5 | import LessonOverviews from 'package/components/LessonOverviews'
6 | import ProposeLesson from './components/ProposeLesson'
7 |
8 | const RequestedLessons = ({
9 | instructor,
10 | lessonsUrl,
11 | technologiesUrl,
12 | }) => (
13 |
14 |
15 |
16 | Requested Lessons
17 |
18 |
19 |
20 | {`Here's some ideas for lessons that you can start recording today. If you claim one of these ideas, you'll have 2 weeks to record a draft and upload it. After that it goes back into the pool for others to claim.`}
21 |
22 |
23 |
34 |
35 |
39 | There are no requested lessons, but you can create your own.
40 |
41 | }
42 | lessonsUrl={lessonsUrl}
43 | />
44 |
45 |
46 | )
47 |
48 | RequestedLessons.propTypes = {
49 | instructor: PropTypes.object.isRequired,
50 | lessonsUrl: PropTypes.string.isRequired,
51 | }
52 |
53 | export default RequestedLessons
54 |
--------------------------------------------------------------------------------
/src/package/components/Tabs/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {Tab, Tabs, TabList, TabPanel, resetIdCounter} from 'react-tabs'
3 | import {map} from 'lodash'
4 |
5 | resetIdCounter()
6 |
7 | class TabsComponent extends Component {
8 |
9 | state = {
10 | selected: 0,
11 | }
12 |
13 | handleSelect = (index) => (
14 | this.setState({
15 | selected: index,
16 | })
17 | )
18 |
19 | render() {
20 | const {groups} = this.props
21 |
22 | return (
23 |
24 |
25 |
26 | {map(groups, (group, index) => (
27 |
46 | {group.title}
47 |
48 | ))}
49 |
50 |
51 | {map(groups, (group, index) => (
52 |
53 | {group.component}
54 |
55 | ))}
56 |
57 |
58 | )
59 | }
60 | }
61 |
62 | TabsComponent.propTypes = {
63 | groups: React.PropTypes.arrayOf(React.PropTypes.shape({
64 | title: React.PropTypes.string.isRequired,
65 | component: React.PropTypes.node.isRequired,
66 | })).isRequired,
67 | }
68 |
69 | export default TabsComponent
70 |
--------------------------------------------------------------------------------
/src/package/utils/colorValues/index.js:
--------------------------------------------------------------------------------
1 | const colorValues = {
2 | 'black': '#000',
3 | 'white': '#fff',
4 | 'transparent': 'transparent',
5 | 'black-90': 'rgba(0, 0, 0, .9)',
6 | 'black-80': 'rgba(0, 0, 0, .8)',
7 | 'black-70': 'rgba(0, 0, 0, .7)',
8 | 'black-60': 'rgba(0, 0, 0, .6)',
9 | 'black-50': 'rgba(0, 0, 0, .5)',
10 | 'black-40': 'rgba(0, 0, 0, .4)',
11 | 'black-30': 'rgba(0, 0, 0, .3)',
12 | 'black-20': 'rgba(0, 0, 0, .2)',
13 | 'black-10': 'rgba(0, 0, 0, .1)',
14 | 'black-05': 'rgba(0, 0, 0, .05)',
15 | 'black-025': 'rgba(0, 0, 0, .025)',
16 | 'black-0125': 'rgba(0, 0, 0, .0125)',
17 | 'white-90': 'rgba(255, 255, 255, .9)',
18 | 'white-80': 'rgba(255, 255, 255, .8)',
19 | 'white-70': 'rgba(255, 255, 255, .7)',
20 | 'white-60': 'rgba(255, 255, 255, .6)',
21 | 'white-50': 'rgba(255, 255, 255, .5)',
22 | 'white-40': 'rgba(255, 255, 255, .4)',
23 | 'white-30': 'rgba(255, 255, 255, .3)',
24 | 'white-20': 'rgba(255, 255, 255, .2)',
25 | 'white-10': 'rgba(255, 255, 255, .1)',
26 | 'white-05': 'rgba(255, 255, 255, .05)',
27 | 'white-025': 'rgba(255, 255, 255, .025)',
28 | 'white-0125': 'rgba(255, 255, 255, .0125)',
29 | 'white-secondary': '#f8f8f8',
30 | 'base': '#232c3b',
31 | 'base-secondary': '#171e27',
32 | 'gray': '#f4f5f9',
33 | 'gray-secondary': '#e2e3e7',
34 | 'dark-gray': '#63768d',
35 | 'dark-gray-secondary': '#B0B6BE',
36 | 'blue': '#4786ff',
37 | 'blue-secondary': '#386fda',
38 | 'dark-blue': '#344055',
39 | 'dark-blue-secondary': '#273040',
40 | 'green': '#59cd90',
41 | 'green-secondary': '#409b6b',
42 | 'orange': '#fd9126',
43 | 'orange-secondary': '#c97420',
44 | 'red': '#f0624d',
45 | 'red-secondary': '#c44c3a',
46 | 'yellow': '#fbc155',
47 | 'yellow-secondary': '#d2a043',
48 | }
49 |
50 | export default colorValues
51 |
--------------------------------------------------------------------------------
/src/package/components/Heading/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import ContainerWidth from 'package/components/ContainerWidth'
3 |
4 | export const levels = ['1', '2', '3', '4', '5']
5 |
6 | const sharedStyle = {
7 | wordBreak: 'break-word',
8 | }
9 |
10 | const Heading = ({children, level}) => (
11 |
12 | {(containerWidth) => (
13 |
14 | {{
15 | 1: (
16 |
20 | {children}
21 |
22 | ),
23 | 2: (
24 |
28 | {children}
29 |
30 | ),
31 | 3: (
32 |
36 | {children}
37 |
38 | ),
39 | 4: (
40 |
44 | {children}
45 |
46 | ),
47 | 5: (
48 |
52 | {children}
53 |
54 | ),
55 | }[level]}
56 |
57 | )}
58 |
59 | )
60 |
61 | Heading.propTypes = {
62 | children: PropTypes.string.isRequired,
63 | level: PropTypes.oneOf(levels).isRequired,
64 | }
65 |
66 | export default Heading
67 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorsDirectory/components/InstructorOverviews/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react'
2 | import Request from 'package/components/Request'
3 | import Paragraph from 'package/components/Paragraph'
4 | import createInstructorsUrlWithParams from './utils/createInstructorsUrlWithParams'
5 | import PaginatedInstructorOverviews from './components/PaginatedInstructorOverviews'
6 |
7 | class InstructorOverviews extends Component {
8 |
9 | static propTypes = {
10 | instructorsUrl: PropTypes.string.isRequired,
11 | pageSize: PropTypes.number,
12 | }
13 |
14 | static defaultProps = {
15 | pageSize: 10,
16 | }
17 |
18 | state = {
19 | currentPage: 1,
20 | }
21 |
22 | handleCurrentPage = (currentPage, request) => {
23 | this.setState({currentPage}, () => {request()})
24 | }
25 |
26 | render() {
27 | const {currentPage} = this.state
28 | const {instructorsUrl, pageSize} = this.props
29 |
30 | return (
31 |
40 | {({request, running, data, response}) => (
41 |
44 | No instructors to show
45 |
46 | }
47 | pageSize={pageSize}
48 | currentPage={currentPage}
49 | total={response.headers['x-total-count']}
50 | instructors={data}
51 | requestNextPage={(nextPage) => {
52 | this.handleCurrentPage(nextPage, request)
53 | }}
54 | />
55 | )}
56 |
57 | )
58 | }
59 | }
60 |
61 | export default InstructorOverviews
62 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/GetPublished/components/Checklist/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {Link} from 'react-router-dom'
3 | import {map} from 'lodash'
4 | import Icon from 'package/components/Icon'
5 | import List from 'package/components/List'
6 | import MoreInfo from './components/MoreInfo'
7 |
8 | const Checklist = ({items}) => (
9 | (
10 |
14 |
15 |
16 |
17 |
18 | {index + 1}
19 |
20 |
21 |
25 | {item.description}
26 |
27 |
28 |
29 |
30 | {item.isComplete
31 | ?
35 | :
36 | {item.moreInfoUrl
37 | ?
38 |
39 |
40 | : null
41 | }
42 | {item.action
43 | ?
47 |
51 |
52 | : null
53 | }
54 |
55 | }
56 |
57 |
58 | ))} />
59 | )
60 |
61 | Checklist.propTypes = {
62 | items: PropTypes.arrayOf(React.PropTypes.shape({
63 | isComplete: PropTypes.bool.isRequired,
64 | description: PropTypes.string.isRequired,
65 | moreInfoUrl: PropTypes.string,
66 | action: PropTypes.string,
67 | })).isRequired,
68 | }
69 |
70 | export default Checklist
71 |
--------------------------------------------------------------------------------
/src/package/components/LessonOverviews/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react'
2 | import createLessonsUrlWithParams from 'package/utils/createLessonsUrlWithParams'
3 | import Request from 'package/components/Request'
4 | import PaginatedLessonOverviews from './components/PaginatedLessonOverviews'
5 |
6 | class LessonOverviews extends Component {
7 |
8 | static propTypes = {
9 | states: PropTypes.array.isRequired,
10 | fallback: PropTypes.node.isRequired,
11 | lessonsUrl: PropTypes.string.isRequired,
12 | pageSize: PropTypes.number,
13 | includeLessonsInCourses: PropTypes.bool,
14 | }
15 |
16 | static defaultProps = {
17 | pageSize: 10,
18 | includeLessonsInCourses: true,
19 | }
20 |
21 | state = {
22 | currentPage: 1,
23 | }
24 |
25 | handleCurrentPage = (currentPage, request) => {
26 | this.setState({currentPage}, () => {request()})
27 | }
28 |
29 | render() {
30 | const {currentPage} = this.state
31 | const {
32 | states,
33 | fallback,
34 | lessonsUrl,
35 | pageSize,
36 | includeLessonsInCourses,
37 | } = this.props
38 |
39 | return (
40 |
50 | {({request, data, response}) => (
51 | {
58 | this.handleCurrentPage(nextPage, request)
59 | }}
60 | requestCurrentPage={request}
61 | />
62 | )}
63 |
64 | )
65 | }
66 | }
67 |
68 | export default LessonOverviews
69 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorsDirectory/components/InstructorOverviews/components/PaginatedInstructorOverviews/components/InstructorOverview/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import {Link} from 'react-router-dom'
4 | import Heading from 'package/components/Heading'
5 | import Avatar from 'package/components/Avatar'
6 | import LessonGroupsStat from './components/LessonGroupsStat'
7 |
8 | export default ({instructor}) => {
9 |
10 | if(!instructor) {
11 | return null
12 | }
13 |
14 | const lessonOverviewsByGroupStats = [
15 | {
16 | label: 'In Progress',
17 | count: instructor.claimed_lessons,
18 | },
19 | {
20 | label: 'In Review',
21 | count: instructor.submitted_lessons,
22 | },
23 | {
24 | label: 'In Queue',
25 | count: instructor.approved_lessons,
26 | },
27 | {
28 | label: 'Published',
29 | count: instructor.published_lessons,
30 | },
31 | ]
32 |
33 | return (
34 |
35 |
36 |
40 |
41 |
42 |
48 |
49 |
50 | {instructor.full_name}
51 |
52 |
53 |
54 |
55 |
56 |
57 | {map(lessonOverviewsByGroupStats, group => (
58 |
62 |
66 |
67 | ))}
68 |
69 |
70 |
71 | )
72 | }
73 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {hasUnlockedPublished} from 'package/utils/instructorMilestones'
3 | import LayoutColumns from 'package/components/LayoutColumns'
4 | import InstructorRevenue from 'package/components/InstructorRevenue'
5 | import InstructorStats from 'package/components/InstructorStats'
6 | import InstructorLessons from 'package/components/InstructorLessons'
7 | import RequestedLessons from 'package/components/RequestedLessons'
8 | import GetPublished from './components/GetPublished'
9 | import Help from './components/Help'
10 |
11 | const InstructorDashboard = ({
12 | instructor,
13 | lessonsUrl,
14 | technologiesUrl,
15 | }) => hasUnlockedPublished(instructor.published_lessons)
16 | ?
17 | ,
20 | ,
21 | ]}
22 | relativeSizes={[2, 1]}
23 | />
24 | ,
27 | ,
32 | ]}
33 | relativeSizes={[2, 1]}
34 | />
35 |
36 | :
37 | ,
39 | ,
44 | ,
45 | ]} />
46 |
47 |
48 |
49 | InstructorDashboard.propTypes = {
50 | instructor: PropTypes.object.isRequired,
51 | lessonsUrl: PropTypes.string.isRequired,
52 | technologiesUrl: PropTypes.string.isRequired,
53 | }
54 |
55 | export default InstructorDashboard
56 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/components/LineChart/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import hexToRgba from 'hex-rgba'
4 | import {Line} from 'react-chartjs-2'
5 | import numberFormattingByType from 'package/utils/numberFormattingByType'
6 | import colorValues from 'package/utils/colorValues'
7 |
8 | const tooltipColor = colorValues['dark-gray']
9 |
10 | const sharedOptions = (currency) => ({
11 | legend: {
12 | display: false,
13 | },
14 | showScale: false,
15 | scales: {
16 | yAxes: [{
17 | display: false,
18 | }],
19 | xAxes: [{
20 | gridLines: {
21 | display: false
22 | },
23 | }],
24 | },
25 | tooltips: {
26 | enabled: true,
27 | mode: 'single',
28 | displayColors: false,
29 | callbacks: {
30 | label: (tooltipItems) => currency
31 | ? numberFormattingByType.money(tooltipItems.yLabel)
32 | : numberFormattingByType.general(tooltipItems.yLabel),
33 | },
34 | titleFontSize: 0,
35 | backgroundColor: 'transparent',
36 | bodyFontColor: tooltipColor,
37 | bodyFontStyle: 'bold',
38 | },
39 | layout: {
40 | padding: {
41 | top: 5,
42 | },
43 | },
44 | })
45 |
46 | const sharedData = (color) => ({
47 | borderColor: color,
48 | backgroundColor: hexToRgba(color, 20),
49 | pointBorderColor: color,
50 | pointHoverBackgroundColor: color,
51 | pointHoverBorderColor: color,
52 | fill: true,
53 | lineTension: 0.0,
54 | borderCapStyle: 'butt',
55 | borderDash: [],
56 | borderDashOffset: 0.0,
57 | borderJoinStyle: 'miter',
58 | pointBackgroundColor: '#fff',
59 | pointBorderWidth: 3,
60 | pointHoverBorderWidth: 3,
61 | pointRadius: 3,
62 | pointHoverRadius: 3,
63 | pointHitRadius: 3,
64 | })
65 |
66 | export default ({xAxis, yAxis, currency = false}) => (
67 |
68 | ({
73 | ...sharedData(set.color),
74 | data: set.points,
75 | }))
76 | }}
77 | />
78 |
79 | )
80 |
--------------------------------------------------------------------------------
/src/package/components/ContainerWidth/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes, Component} from 'react'
2 | import elementResizeEvent, {unbind} from 'element-resize-event'
3 | import {
4 | smallContainerWidth,
5 | mediumContainerWidth,
6 | largeContainerWidth,
7 | xlargeContainerWidth,
8 | xxlargeContainerWidth,
9 | } from 'package/utils/hardCodedSizes'
10 |
11 | export default class ContainerWidth extends Component {
12 |
13 | state = {
14 | containerWidth: 'small',
15 | }
16 |
17 | componentDidMount = () => {
18 | this.handleWidthChange()
19 | elementResizeEvent(this.refs.container, this.handleWidthChange)
20 | }
21 |
22 | componentWillUnmount = () => {
23 | unbind(this.refs.container)
24 | }
25 |
26 | handleWidthChange = () => {
27 | const {onWidthChange} = this.props
28 | if(onWidthChange) {
29 | onWidthChange()
30 | }
31 | const containerExactWidth = this.refs.container.clientWidth
32 | if(containerExactWidth >= xxlargeContainerWidth) {
33 | this.setState({
34 | containerWidth: 'xxlarge',
35 | })
36 | }
37 | else if(containerExactWidth >= xlargeContainerWidth) {
38 | this.setState({
39 | containerWidth: 'xlarge',
40 | })
41 | }
42 | else if(containerExactWidth >= largeContainerWidth) {
43 | this.setState({
44 | containerWidth: 'large',
45 | })
46 | }
47 | else if (containerExactWidth >= mediumContainerWidth) {
48 | this.setState({
49 | containerWidth: 'medium',
50 | })
51 | }
52 | else if (containerExactWidth >= smallContainerWidth) {
53 | this.setState({
54 | containerWidth: 'small',
55 | })
56 | }
57 | else {
58 | this.setState({
59 | containerWidth: 'xsmall',
60 | })
61 | }
62 | }
63 |
64 | render() {
65 | const {containerWidth} = this.state
66 | const {children, className} = this.props
67 | return (
68 |
72 | {children(containerWidth)}
73 |
74 | )
75 | }
76 | }
77 |
78 | ContainerWidth.propTypes = {
79 | children: PropTypes.func.isRequired,
80 | onWidthChange: PropTypes.func,
81 | }
82 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/Help/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map} from 'lodash'
3 | import {guideUrl, chatUrl, instructorsChatUrl} from 'package/utils/urls'
4 | import {hasUnlockedPublished} from 'package/utils/instructorMilestones'
5 | import Maybe from 'package/components/Maybe'
6 | import Paragraph from 'package/components/Paragraph'
7 | import List from 'package/components/List'
8 | import Heading from 'package/components/Heading'
9 | import HeaderCard from 'package/components/HeaderCard'
10 | import Anchor from 'package/components/Anchor'
11 |
12 | const items=[
13 | {
14 | title: 'Instructor 101 Guide',
15 | description: `We've put together a written Instructor Guide with step by step details on creating egghead lessons and courses. It is required reading for all instructors, and will help you make great lessons.`,
16 | url: guideUrl,
17 | action: 'View the guide',
18 | },
19 | {
20 | title: 'Mentors',
21 | description: 'Joel, Trevor, and Zac can help with anything related to egghead.io.',
22 | url: chatUrl,
23 | action: 'Reach out in Slack',
24 | },
25 | {
26 | title: 'Slack',
27 | description: `The egghead-instructors Slack channel is available for instructors to chat and see each other's progress.`,
28 | url: instructorsChatUrl,
29 | action: 'View the channel',
30 | },
31 | ]
32 |
33 | const Help = ({publishedLessons}) => (
34 |
35 |
40 | (
41 |
42 |
43 | {item.title}
44 |
45 |
46 | {item.description}
47 |
48 |
52 | {item.action}
53 |
54 |
55 | ))} />
56 |
57 |
58 | )
59 |
60 | export default Help
61 |
--------------------------------------------------------------------------------
/src/package/components/InstructorStats/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import pluralize from 'pluralize'
3 | import {map, uniqBy} from 'lodash'
4 | import {hasUnlockedPublished, hasUnlockedCoursePublished} from 'package/utils/instructorMilestones'
5 | import createLessonsUrlWithParams from 'package/utils/createLessonsUrlWithParams'
6 | import Card from 'package/components/Card'
7 | import Request from 'package/components/Request'
8 | import InstructorStat from './components/InstructorStat'
9 |
10 | const InstructorStats = ({instructor}) => {
11 |
12 | if(!hasUnlockedPublished(instructor.published_lessons)) {
13 | return null
14 | }
15 |
16 | return (
17 |
18 |
19 |
20 |
25 | {({data}) => (
26 | ({
30 | name: lesson.technology.name,
31 | graphicUrl: lesson.technology.logo_http_url,
32 | httpUrl: lesson.technology.http_url,
33 | })), technology => technology.name)}
34 | />
35 | )}
36 |
37 |
38 | {hasUnlockedCoursePublished(instructor.published_courses)
39 | ?
40 |
41 | {({data}) => (
42 | ({
46 | name: course.title,
47 | graphicUrl: course.square_cover_url,
48 | httpUrl: course.http_url,
49 | }))}
50 | />
51 | )}
52 |
53 |
54 | : null
55 | }
56 |
57 |
58 |
59 | )
60 | }
61 |
62 | InstructorStats.propTypes = {
63 | instructor: PropTypes.object.isRequired,
64 | }
65 |
66 | export default InstructorStats
67 |
--------------------------------------------------------------------------------
/src/package/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {keys} from 'lodash'
3 | import hexToRgba from 'hex-rgba'
4 | import styled from 'styled-components'
5 | import colors from 'package/utils/colors'
6 | import colorValues from 'package/utils/colorValues'
7 |
8 | const verticalPaddingBySize = {
9 | 'small': {
10 | paddingTop: '8px',
11 | paddingBottom: '8px',
12 | },
13 | 'medium': {
14 | paddingTop: '12px',
15 | paddingBottom: '12px',
16 | },
17 | 'large': {
18 | paddingTop: '16px',
19 | paddingBottom: '16px',
20 | },
21 | 'xlarge': {
22 | paddingTop: '20px',
23 | paddingBottom: '20px',
24 | },
25 | }
26 |
27 | export const sizes = keys(verticalPaddingBySize)
28 |
29 | const StyledButton = styled.button`
30 | min-width: ${props => props.size === 'small' ? '0px' : '200px'};
31 | padding-left: ${props => props.size === 'small' ? '24px' : '36px'};
32 | padding-right: ${props => props.size === 'small' ? '24px' : '36px'};
33 | padding-top: ${props => verticalPaddingBySize[props.size].paddingTop};
34 | padding-bottom: ${props => verticalPaddingBySize[props.size].paddingBottom};
35 | transition: box-shadow 0.3s ease-in-out;
36 |
37 | &:hover,
38 | &:active,
39 | &:focus {
40 | box-shadow: 0px 8px 12px 0px ${props =>
41 | hexToRgba(
42 | colorValues['base-secondary'],
43 | props.overDark ? 70 : 20
44 | )};
45 | }
46 | `
47 |
48 | const Button = ({
49 | children,
50 | onClick,
51 | size,
52 | color,
53 | outline,
54 | overDark,
55 | }) => (
56 |
72 | {children}
73 |
74 | )
75 |
76 | Button.propTypes = {
77 | children: PropTypes.node.isRequired,
78 | onClick: PropTypes.func,
79 | size: PropTypes.oneOf(sizes),
80 | color: PropTypes.oneOf(colors),
81 | outline: PropTypes.bool,
82 | overDark: PropTypes.bool,
83 | }
84 |
85 | Button.defaultProps = {
86 | size: 'medium',
87 | color: 'orange',
88 | outline: false,
89 | overDark: false,
90 | }
91 |
92 | export default Button
93 |
--------------------------------------------------------------------------------
/src/package/components/Toggle/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react'
2 | import {first} from 'lodash'
3 |
4 | export const selectedItems = ['left', 'right']
5 |
6 | const sharedLabelClasses = 'w-50 normal lh-copy tc relative z-1 pv2 pointer border-box'
7 |
8 | const Highlight = ({selectedItem}) => {
9 | return (
10 |
15 |
16 |
17 | )
18 | }
19 |
20 | class Toggle extends Component {
21 |
22 | state = {
23 | selectedItem: this.props.selectedItem
24 | }
25 |
26 | handleChange = (e) => {
27 | const { selectedItem } = this.state
28 | this.setState({ selectedItem: selectedItem === 'left' ? 'right' : 'left' })
29 | }
30 |
31 | render() {
32 | const {rightOption, leftOption, onClick} = this.props
33 | const {selectedItem} = this.state
34 |
35 | return (
36 |
37 |
41 | {leftOption}
42 |
43 |
44 |
45 | {rightOption}
46 |
47 |
48 |
49 |
50 |
51 | )
52 | }
53 | }
54 |
55 | Toggle.propTypes = {
56 | leftOption: PropTypes.string.isRequired,
57 | rightOption: PropTypes.string.isRequired,
58 | onClick: PropTypes.func,
59 | selectedItem: PropTypes.oneOf(selectedItems),
60 | }
61 |
62 | Toggle.defaultProps = {
63 | selectedItem: first(selectedItems),
64 | }
65 |
66 | export default Toggle
67 |
--------------------------------------------------------------------------------
/src/package/components/Request/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component, PropTypes} from 'react'
2 | import {includes, first} from 'lodash'
3 | import logout from 'package/utils/logout'
4 | import windowMock from 'package/utils/windowMock'
5 | import RequestBase from './components/RequestBase'
6 |
7 | const universalWindow = typeof(window) === 'undefined'
8 | ? windowMock
9 | : window
10 |
11 | export const methods = [
12 | 'get',
13 | 'post',
14 | 'put',
15 | 'delete',
16 | ]
17 |
18 | class Request extends Component {
19 |
20 | getHeaders = () => {
21 | const headers = {...this.props.headers}
22 | if (this.props.auth) {
23 | return {
24 | ...headers,
25 | Authorization: universalWindow.localStorage.token
26 | ? `Bearer ${universalWindow.localStorage.token}`
27 | : null,
28 | 'Content-Type': 'application/json',
29 | }
30 | }
31 |
32 | return headers
33 | }
34 |
35 | handleError = (error) => {
36 | if (includes([401, 403], error.response.status) && this.props.auth) {
37 | logout()
38 | }
39 | if (this.props.onError) {
40 | this.props.onError(error)
41 | }
42 | }
43 |
44 | render() {
45 | const {url, children, ...rest} = this.props
46 | return (
47 |
53 | {({request, running, error, data, response}) => {
54 | return children({
55 | request,
56 | running,
57 | error,
58 | data,
59 | response,
60 | })
61 | }}
62 |
63 | )
64 | }
65 | }
66 |
67 | Request.propTypes = {
68 | children: PropTypes.func.isRequired,
69 | url: PropTypes.string.isRequired,
70 | lazy: PropTypes.bool,
71 | auth: PropTypes.bool,
72 | placeholder: PropTypes.node,
73 | params: PropTypes.object,
74 | headers: PropTypes.object,
75 | body: PropTypes.object,
76 | onResponse: PropTypes.func,
77 | onData: PropTypes.func,
78 | onError: PropTypes.func,
79 | method: PropTypes.oneOf(methods),
80 | subscribe: PropTypes.bool,
81 | subscribeInterval: PropTypes.number,
82 | }
83 |
84 | Request.defaultProps = {
85 | method: first(methods),
86 | subscribe: false,
87 | auth: false,
88 | subscribeInterval: 10000,
89 | }
90 |
91 | export default Request
92 |
--------------------------------------------------------------------------------
/src/App/components/Resource/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {compact, size} from 'lodash'
3 | import Heading from 'package/components/Heading'
4 | import Paragraph from 'package/components/Paragraph'
5 | import Tabs from 'package/components/Tabs'
6 | import Markdown from 'package/components/Markdown'
7 | import Types from './components/Types'
8 | import Examples, {optOuts} from './components/Examples'
9 |
10 | const Resource = ({name, resource}) => (
11 |
12 |
17 |
18 |
19 |
20 | Use Case
21 |
22 |
23 | {resource.useCase}
24 |
25 |
26 |
27 |
28 |
29 | Import
30 |
31 |
32 | {`\`import {${name}} from 'egghead-ui'\``}
33 |
34 |
35 |
36 | {resource.types && size(resource.types) > 0
37 | ?
41 | : null
42 | }
43 |
44 | {resource.childrenTypes && size(resource.childrenTypes) > 0
45 | ?
49 | : null
50 | }
51 |
52 |
53 | ),
54 | },
55 | {
56 | title: 'Examples',
57 | component: (
58 |
59 |
63 |
64 | ),
65 | },
66 | ])} />
67 |
68 | )
69 |
70 | Resource.propTypes = {
71 | name: PropTypes.string.isRequired,
72 | resource: React.PropTypes.shape({
73 | useCase: PropTypes.string.isRequired,
74 | types: PropTypes.object,
75 | childrenTypes: PropTypes.object,
76 | createExamples: PropTypes.func.isRequired,
77 | optOut: PropTypes.arrayOf(PropTypes.oneOf(optOuts)),
78 | }).isRequired,
79 | }
80 |
81 | export default Resource
82 |
--------------------------------------------------------------------------------
/src/package/components/LessonOverviews/components/PaginatedLessonOverviews/components/LessonOverview/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Link} from 'react-router-dom'
3 | import {detailsByLessonState} from 'package/utils/lessonStates'
4 | import {xsmallContainerWidth} from 'package/utils/hardCodedSizes'
5 | import Maybe from 'package/components/Maybe'
6 | import Heading from 'package/components/Heading'
7 | import Image from 'package/components/Image'
8 | import Avatar from 'package/components/Avatar'
9 | import LessonActions from 'package/components/LessonActions'
10 |
11 | export default ({lesson, requestCurrentPage}) => (
12 |
13 |
14 |
22 |
23 |
28 |
29 |
30 |
31 |
32 | {detailsByLessonState[lesson.state].title || lesson.state}
33 |
34 |
35 |
39 |
40 | {lesson.title}
41 |
42 |
43 |
44 |
45 |
46 |
50 |
51 |
56 |
57 | {lesson.instructor.full_name}
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
73 |
77 |
78 |
79 |
80 | )
81 |
--------------------------------------------------------------------------------
/src/package/screens/LessonDetails/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {map, compact} from 'lodash'
3 | import Markdown from 'package/components/Markdown'
4 | import Heading from 'package/components/Heading'
5 | import List from 'package/components/List'
6 | import Image from 'package/components/Image'
7 | import HeaderCard from 'package/components/HeaderCard'
8 | import LessonActions from 'package/components/LessonActions'
9 | import Avatar from 'package/components/Avatar'
10 | import LessonState from './components/LessonState'
11 | import WistiaVideo from './components/WistiaVideo'
12 |
13 | const LessonDetails = ({lesson, requestLesson}) => {
14 |
15 | const items = compact([
16 | {
17 | title: 'Video',
18 | children: (
19 |
20 | ),
21 | },
22 | {
23 | title: 'State',
24 | children: (
25 |
26 | ),
27 | },
28 | {
29 | title: 'Actions',
30 | children: (
31 |
35 | ),
36 | },
37 | lesson.state === 'requested'
38 | ? null
39 | : {
40 | title: 'Instructor',
41 | children: (
42 |
43 |
47 |
48 | {lesson.instructor.full_name}
49 |
50 |
51 | ),
52 | },
53 | {
54 | title: 'Technology',
55 | children: (
56 |
57 |
62 | {lesson.technology.label}
63 |
64 | ),
65 | },
66 | {
67 | title: 'Summary',
68 | children: (
69 |
70 | {lesson.summary}
71 |
72 | ),
73 | },
74 | ])
75 |
76 | return (
77 |
78 | (
79 |
80 |
81 | {item.title}
82 |
83 |
84 | {item.children}
85 |
86 |
87 | ))} />
88 |
89 | )
90 | }
91 |
92 | LessonDetails.propTypes = {
93 | lesson: PropTypes.object.isRequired,
94 | requestLesson: PropTypes.func.isRequired,
95 | }
96 |
97 | export default LessonDetails
98 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorsDirectory/components/InstructorOverviews/components/PaginatedInstructorOverviews/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import ReactPaginate from 'react-paginate'
3 | import {map} from 'lodash'
4 | import Maybe from 'package/components/Maybe'
5 | import Card from 'package/components/Card'
6 | import InstructorOverview from './components/InstructorOverview'
7 |
8 | const PaginatedInstructorOverviews = ({
9 | fallback,
10 | pageSize,
11 | currentPage,
12 | total,
13 | instructors,
14 | requestNextPage,
15 | }) => {
16 |
17 | const pageCount = Math.ceil(total / pageSize)
18 | const hasMoreThanOnePage = (pageCount > 1) && (instructors.length > 0)
19 |
20 | const linkClassNames = {
21 | mobileHide: 'dn db-ns',
22 | link: 'db dim pointer bg-orange white mb2 mr2 pa2 ph3-ns br2',
23 | }
24 |
25 | return total > 0
26 | ?
27 |
28 | {map(instructors, (instructor, index) => (
29 |
33 |
34 |
35 |
36 |
37 | ))}
38 |
39 |
40 |
41 | {
49 | const {selected} = page
50 | if (currentPage !== selected + 1) {
51 | requestNextPage(selected + 1)
52 | }
53 | }}
54 | containerClassName='mb0 pa0 list mt4 flex flex-wrap items-center'
55 | previousClassName={linkClassNames.mobileHide}
56 | nextClassName={linkClassNames.mobileHide}
57 | disabledClassName='o-20'
58 | previousLinkClassName={linkClassNames.link}
59 | nextLinkClassName={linkClassNames.link}
60 | pageLinkClassName={linkClassNames.link}
61 | activeClassName='o-50'
62 | breakClassName='mr2'
63 | />
64 |
65 |
66 |
67 |
68 | : fallback
69 | }
70 |
71 | PaginatedInstructorOverviews.propTypes = {
72 | fallback: PropTypes.node.isRequired,
73 | pageSize: PropTypes.number.isRequired,
74 | currentPage: PropTypes.number.isRequired,
75 | total: PropTypes.string,
76 | instructors: PropTypes.array.isRequired,
77 | requestNextPage: PropTypes.func.isRequired,
78 | }
79 |
80 | export default PaginatedInstructorOverviews
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "egghead-ui",
3 | "version": "7.0.2",
4 | "description": "Components used across egghead projects",
5 | "homepage": "https://styleguide.egghead.io",
6 | "engines": {
7 | "node": ">=7.0.0"
8 | },
9 | "main": "lib/index.js",
10 | "files": [
11 | "package.json",
12 | "README.md",
13 | "lib"
14 | ],
15 | "scripts": {
16 | "dev:package": "yarn build:package -- -w",
17 | "dev:app": "REACT_APP_EGGHEAD_BASE_URL=http://egghead.dev:5000 NODE_PATH=./src BROWSER=none react-scripts start",
18 | "dev:app:staging": "REACT_APP_EGGHEAD_BASE_URL=https://egghead-io-staging.com NODE_PATH=./src BROWSER=none react-scripts start",
19 | "test": "NODE_PATH=./src react-scripts test",
20 | "lint": "NODE_PATH=./src eslint src",
21 | "build:package": "NODE_PATH=./src babel src/package -d lib -i '**/*.test.js'",
22 | "build:app": "REACT_APP_EGGHEAD_BASE_URL=https://egghead-io-staging.com NODE_PATH=./src react-scripts build",
23 | "verify": "CI=true yarn test && yarn lint && yarn build:package && yarn build:app",
24 | "bump": "yarn version && git push && git push --tags",
25 | "prepublish": "yarn build:package",
26 | "build": "yarn build:app",
27 | "start": "NODE_PATH=./src react-scripts start"
28 | },
29 | "eslintConfig": {
30 | "extends": "react-app"
31 | },
32 | "babel": {
33 | "presets": [
34 | "babel-preset-react",
35 | "env"
36 | ],
37 | "plugins": [
38 | "transform-class-properties",
39 | "transform-object-rest-spread",
40 | [
41 | "module-resolver",
42 | {
43 | "root": [
44 | "./src"
45 | ]
46 | }
47 | ]
48 | ]
49 | },
50 | "peerDependencies": {
51 | "react": "^15.5.4",
52 | "react-dom": "^15.5.4"
53 | },
54 | "devDependencies": {
55 | "babel-cli": "^6.24.1",
56 | "babel-plugin-module-resolver": "^2.7.0",
57 | "babel-plugin-transform-class-properties": "^6.24.1",
58 | "babel-plugin-transform-object-rest-spread": "^6.23.0",
59 | "babel-preset-env": "^1.4.0",
60 | "babel-preset-react": "^6.24.1",
61 | "react": "^15.5.4",
62 | "react-dom": "^15.5.4",
63 | "react-scripts": "^0.9.5"
64 | },
65 | "dependencies": {
66 | "axios": "^0.16.1",
67 | "chart.js": "chartjs/Chart.js",
68 | "element-resize-event": "^2.0.9",
69 | "eslint": "^3.19.0",
70 | "faker": "^4.1.0",
71 | "format-number": "^2.0.2",
72 | "hex-rgba": "^1.0.1",
73 | "jwt-simple": "^0.5.1",
74 | "lodash": "^4.17.4",
75 | "react-chartjs-2": "^2.1.0",
76 | "react-icons": "github:eggheadio/react-icons#3.8.0",
77 | "react-markdown": "^2.5.0",
78 | "react-paginate": "^4.4.2",
79 | "react-router": "^4.1.1",
80 | "react-router-dom": "^4.1.1",
81 | "react-tabs": "^1.0.0",
82 | "styled-components": "^1.4.6",
83 | "tachyons-egghead": "^2.3.1"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/package/screens/InstructorDashboard/components/GetPublished/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {map, uniq, compact, isString, size, filter} from 'lodash'
3 | import {chatInfoUrl, roughDraftInfoUrl, gearSetupInfoUrl} from 'package/utils/urls'
4 | import createLessonsUrlWithParams from 'package/utils/createLessonsUrlWithParams'
5 | import Request from 'package/components/Request'
6 | import HeaderCard from 'package/components/HeaderCard'
7 | import isStepComplete from './utils/isStepComplete'
8 | import Progress from './components/Progress'
9 | import Checklist from './components/Checklist'
10 |
11 | const GetPublished = ({instructor}) => (
12 |
18 | {({data}) => {
19 | const instructorLessonStates = compact(uniq(map(data, 'state')))
20 |
21 | const checklistItems = [
22 | {
23 | isComplete: true,
24 | description: 'Create instructor account',
25 | },
26 | {
27 | isComplete: isString(instructor.slack_id),
28 | description: 'Join egghead Slack',
29 | moreInfoUrl: chatInfoUrl,
30 | },
31 | {
32 | isComplete: isStepComplete(instructorLessonStates, 'claimed'),
33 | description: 'Claim new lesson',
34 | action: '/lessons/new',
35 | },
36 | {
37 | isComplete: isStepComplete(instructorLessonStates, 'submitted'),
38 | description: 'Submit rough draft',
39 | moreInfoUrl: roughDraftInfoUrl,
40 | },
41 | {
42 | isComplete: isString(instructor.gear_tracking_id),
43 | description: 'Get gear',
44 | moreInfoUrl: gearSetupInfoUrl,
45 | },
46 | {
47 | isComplete: isStepComplete(instructorLessonStates, 'updated'),
48 | description: 'Re-record with gear',
49 | moreInfoUrl: roughDraftInfoUrl,
50 | },
51 | {
52 | isComplete: isStepComplete(instructorLessonStates, 'approved'),
53 | description: 'Iterate until approved',
54 | moreInfoUrl: roughDraftInfoUrl,
55 | },
56 | {
57 | isComplete: isStepComplete(instructorLessonStates, 'published'),
58 | description: 'Publish lesson',
59 | },
60 | ]
61 |
62 | return (
63 |
71 | }
72 | >
73 |
74 |
75 | )
76 | }}
77 |
78 | )
79 |
80 | export default GetPublished
81 |
--------------------------------------------------------------------------------
/src/package/components/LessonOverviews/components/PaginatedLessonOverviews/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import ReactPaginate from 'react-paginate'
3 | import {map} from 'lodash'
4 | import Maybe from 'package/components/Maybe'
5 | import Card from 'package/components/Card'
6 | import sortLessonsByState from './utils/sortLessonsByState'
7 | import LessonOverview from './components/LessonOverview'
8 |
9 | const PaginatedLessonOverviews = ({
10 | fallback,
11 | pageSize,
12 | currentPage,
13 | total,
14 | lessons,
15 | requestNextPage,
16 | requestCurrentPage,
17 | }) => {
18 |
19 | const pageCount = Math.ceil(total / pageSize)
20 | const hasMoreThanOnePage = (pageCount > 1) && (lessons.length > 0)
21 |
22 | const linkClassNames = {
23 | mobileHide: 'dn db-ns',
24 | link: 'db dim pointer bg-orange white mb2 mr2 pa2 ph3-ns br2',
25 | }
26 |
27 | return total > 0
28 | ?
29 |
30 | {map(sortLessonsByState(lessons), (lesson, index) => (
31 |
35 |
36 |
40 |
41 |
42 | ))}
43 |
44 |
45 |
46 | {
54 | const {selected} = page
55 | if (currentPage !== selected + 1) {
56 | requestNextPage(selected + 1)
57 | }
58 | }}
59 | containerClassName='mb0 pa0 list mt4 flex flex-wrap items-center'
60 | previousClassName={linkClassNames.mobileHide}
61 | nextClassName={linkClassNames.mobileHide}
62 | disabledClassName='o-20'
63 | previousLinkClassName={linkClassNames.link}
64 | nextLinkClassName={linkClassNames.link}
65 | pageLinkClassName={linkClassNames.link}
66 | activeClassName='o-50'
67 | breakClassName='mr2'
68 | />
69 |
70 |
71 |
72 |
73 | : fallback
74 | }
75 |
76 | PaginatedLessonOverviews.propTypes = {
77 | fallback: PropTypes.node.isRequired,
78 | pageSize: PropTypes.number.isRequired,
79 | currentPage: PropTypes.number.isRequired,
80 | total: PropTypes.string,
81 | lessons: PropTypes.array.isRequired,
82 | requestNextPage: PropTypes.func.isRequired,
83 | }
84 |
85 | export default PaginatedLessonOverviews
86 |
--------------------------------------------------------------------------------
/src/package/utils/lessonStates/index.js:
--------------------------------------------------------------------------------
1 | import {keys} from 'lodash'
2 |
3 | export const detailsByLessonState = {
4 | proposed: {
5 | action: 'Propose',
6 | description: 'This lesson is proposed and is waiting for review to be accepted.',
7 | color: 'blue',
8 | icon: 'add',
9 | },
10 | cancelled: {
11 | action: 'Cancel',
12 | description: 'This lesson has been cancelled.',
13 | color: 'red',
14 | icon: 'cancel',
15 | },
16 | accepted: {
17 | action: 'Accept',
18 | description: 'This lesson has been accepted - it can be claimed.',
19 | color: 'green',
20 | icon: 'add',
21 | },
22 | requested: {
23 | action: 'Request',
24 | description: 'This lesson has been requested - it can be claimed.',
25 | color: 'dark-gray',
26 | icon: 'add',
27 | },
28 | claimed: {
29 | action: 'Claim',
30 | description: 'This lesson is claimed. Now it needs a video to be added and lesson details filled out.',
31 | color: 'green',
32 | icon: 'add',
33 | },
34 | submitted: {
35 | action: 'Submit',
36 | description: 'Sweet! This lesson has been submitted and is waiting for review to be approved.',
37 | color: 'green',
38 | icon: 'add',
39 | },
40 | rejected: {
41 | action: 'Ask for Changes',
42 | description: 'Changes have been requested for this lesson. Please update the lesson as requested.',
43 | title:'Changes Needed',
44 | color: 'orange',
45 | icon: 'cancel',
46 | },
47 | updated: {
48 | action: 'Apply Update',
49 | description: 'Lesson updated! Waiting for review to be approved.',
50 | color: 'green',
51 | icon: 'refresh',
52 | },
53 | approved: {
54 | action: 'Approve',
55 | description: 'This lesson has been approved.',
56 | color: 'green',
57 | icon: 'add',
58 | },
59 | published: {
60 | action: 'Publish',
61 | description: `This lesson has been published. It is available publicly for students to view on egghead.io.`,
62 | color: 'green',
63 | icon: 'add',
64 | },
65 | flagged: {
66 | action: 'Flag',
67 | description: `This lesson has been flagged - it needs to be revised or it will need to be retired.`,
68 | color: 'orange',
69 | icon: 'warning',
70 | },
71 | revised: {
72 | action: 'Revise',
73 | description: `This lesson has revised and is no longer flagged.`,
74 | color: 'green',
75 | icon: 'refresh',
76 | },
77 | retired: {
78 | action: 'Retire',
79 | description: `This lesson has been retired.`,
80 | color: 'orange',
81 | icon: 'cancel',
82 | },
83 | }
84 |
85 | export const lessonStates = keys(detailsByLessonState)
86 |
87 | export const lessonStateVerbToPastTense = {
88 | propose: 'proposed',
89 | cancel: 'cancelled',
90 | accept: 'accepted',
91 | request: 'requested',
92 | claim: 'claimed',
93 | submit: 'submitted',
94 | reject: 'rejected',
95 | apply_update: 'updated',
96 | approve: 'approved',
97 | publish: 'published',
98 | flag: 'flagged',
99 | revise: 'revised',
100 | retire: 'retired',
101 | }
102 |
--------------------------------------------------------------------------------
/src/App/index.js:
--------------------------------------------------------------------------------
1 | import 'tachyons-egghead'
2 | import React from 'react'
3 | import {map} from 'lodash'
4 | import {BrowserRouter, Route, Switch} from 'react-router-dom'
5 | import resourcesByType from './utils/resourcesByType'
6 | import Navigation from './components/Navigation'
7 | import Main from './components/Main'
8 | import Readme from './components/Readme'
9 | import Colors from './components/Colors'
10 | import Icons from './components/Icons'
11 | import ResourceDirectory from './components/ResourceDirectory'
12 | import Resource from './components/Resource'
13 | import Contributing from './components/Contributing'
14 |
15 | const navigationItems = [
16 | {
17 | label: 'Readme',
18 | path: '/',
19 | children: ,
20 | exact: true,
21 | },
22 | {
23 | label: 'Colors',
24 | path: '/colors',
25 | children: ,
26 | },
27 | {
28 | label: 'Icons',
29 | path: '/icons',
30 | children: ,
31 | },
32 | {
33 | label: 'Components',
34 | path: resourcesByType['components'].urlBase,
35 | children: ,
36 | },
37 | {
38 | label: 'Screens',
39 | path: resourcesByType['screens'].urlBase,
40 | children: ,
41 | },
42 | {
43 | label: 'Utilities',
44 | path: resourcesByType['utilities'].urlBase,
45 | children: ,
46 | },
47 | {
48 | label: 'Contributing',
49 | path: '/contributing',
50 | children: ,
51 | },
52 | ]
53 |
54 | const App = () => (
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | {map(resourcesByType, (value, key) => {
63 | const resources = resourcesByType[key]
64 | return (
65 | {
69 | const {name} = match.params
70 | return (
71 |
72 |
76 |
77 | )
78 | }}
79 | />
80 | )
81 | })}
82 |
83 | {map(navigationItems, (navigationItem) => (
84 | (
89 |
90 | {navigationItem.children}
91 |
92 | )}
93 | />
94 | ))}
95 |
96 | (
97 |
98 | Check your URL
99 |
100 | )} />
101 |
102 |
103 |
104 |
105 |
106 | )
107 |
108 | export default App
109 |
--------------------------------------------------------------------------------
/public/safari-pinned-tab.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 | Created by potrace 1.11, written by Peter Selinger 2001-2013
9 |
10 |
12 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/src/package/components/Request/components/RequestBase/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import axios from 'axios'
3 | import Error from 'package/components/Error'
4 | import Loading from 'package/components/Loading'
5 |
6 | const http = axios.create()
7 |
8 | class RequestBase extends Component {
9 |
10 | state = {
11 | running: !this.props.lazy,
12 | response: null,
13 | data: null,
14 | error: null,
15 | subscription: null,
16 | }
17 |
18 | componentDidMount() {
19 | const {lazy, subscribe, subscribeInterval} = this.props
20 | if (!lazy) {
21 | this.request()
22 | if (subscribe) {
23 | this.setState({
24 | subscription: setInterval(() => {
25 | const {running} = this.state
26 | if(!running) {
27 | this.request()
28 | }
29 | }, subscribeInterval)
30 | })
31 | }
32 | }
33 | }
34 |
35 | componentWillUnmount() {
36 | this.willUnmount = true
37 | const {subscription} = this.state
38 | clearInterval(subscription)
39 | }
40 |
41 | request = (body = this.props.body) => {
42 |
43 | const {response, data} = this.state
44 |
45 | const {
46 | method,
47 | url,
48 | params,
49 | headers,
50 | onResponse,
51 | onData,
52 | onError,
53 | } = this.props
54 |
55 | this.setState({
56 | running: true,
57 | request: true
58 | }, () => {
59 | http.request({
60 | method: method,
61 | url: url,
62 | params: params,
63 | headers: headers,
64 | data: body,
65 | })
66 | .then(response => {
67 | if (this.willUnmount) {
68 | return
69 | }
70 | this.setState({
71 | running: false,
72 | response,
73 | data: response.data,
74 | error: null,
75 | }, () => {
76 | if (onResponse) {
77 | onResponse(null, response)
78 | }
79 | if (onData) {
80 | onData(data)
81 | }
82 | })
83 | })
84 | .catch(error => {
85 | if (this.willUnmount) {
86 | return
87 | }
88 | this.setState({
89 | running: false,
90 | response: error,
91 | error,
92 | }, () => {
93 | if (onResponse) {
94 | onResponse(response)
95 | }
96 | if (onError) {
97 | onError(error)
98 | }
99 | })
100 | })
101 | })
102 | }
103 |
104 | render() {
105 | const {children, lazy, placeholder, showLoadingBetweenRequests} = this.props
106 | const {running, error, data, response} = this.state
107 | if (!children) {
108 | return null
109 | }
110 | if ((running && showLoadingBetweenRequests) || (running && (lazy || !data))) {
111 | return placeholder ? placeholder :
112 | }
113 | if (error) {
114 | return (
115 |
116 | {`Error: ${error.message}`}
117 |
118 | )
119 | }
120 | return children({
121 | request: this.request,
122 | running,
123 | error,
124 | data,
125 | response,
126 | }) || null
127 | }
128 | }
129 |
130 | export default RequestBase
131 |
--------------------------------------------------------------------------------
/src/package/components/LessonOverviewsByGroup/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {map, compact} from 'lodash'
3 | import {publicLessonsUrl} from 'package/utils/urls'
4 | import {selfReviewThreshold, hasUnlockedSelfReview} from 'package/utils/instructorMilestones'
5 | import Maybe from 'package/components/Maybe'
6 | import Tabs from 'package/components/Tabs'
7 | import LessonOverviews from 'package/components/LessonOverviews'
8 | import Prompt from 'package/components/Prompt'
9 |
10 | const LessonOverviewsByGroup = ({lessonsUrl, instructor}) => {
11 |
12 | const items = compact([
13 | {
14 | title: 'In Progress',
15 | states: [
16 | 'accepted',
17 | 'claimed',
18 | 'rejected',
19 | ],
20 | includeLessonsInCourses: true,
21 | },
22 | {
23 | title: 'In Review',
24 | description: (
25 |
26 |
27 | These lessons are waiting for review to proceed.
28 |
29 |
30 |
31 | {` `}
32 | Since you have {selfReviewThreshold}+ lessons published, you can review your own lessons.
33 |
34 |
35 |
36 | ),
37 | states: [
38 | 'proposed',
39 | 'submitted',
40 | 'updated',
41 | ],
42 | includeLessonsInCourses: true,
43 | },
44 | {
45 | title: 'In Queue',
46 | description: 'These lessons are in the publishing queue. The queue automatically publishes them from top to bottom. Lessons that are in a course are not shown here because they are held back until the entire course is published.',
47 | states: [
48 | 'approved'
49 | ],
50 | includeLessonsInCourses: false,
51 | },
52 | ])
53 |
54 | return (
55 | ({
57 | title: item.title,
58 | component: (
59 |
60 |
61 |
62 | {item.description}
63 |
64 |
65 |
66 |
74 | }
75 | lessonsUrl={lessonsUrl}
76 | includeLessonsInCourses={item.includeLessonsInCourses}
77 | />
78 |
79 |
80 | ),
81 | })),
82 | {
83 | title: 'Published',
84 | component: (
85 |
92 | ),
93 | },
94 | ]} />
95 | )
96 | }
97 |
98 | LessonOverviewsByGroup.propTypes = {
99 | lessonsUrl: PropTypes.string.isRequired,
100 | instructor: PropTypes.object,
101 | }
102 |
103 | export default LessonOverviewsByGroup
104 |
--------------------------------------------------------------------------------
/src/package/components/LessonActions/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {map, keys, compact} from 'lodash'
3 | import {lessonStateVerbToPastTense, detailsByLessonState} from 'package/utils/lessonStates'
4 | import Request from 'package/components/Request'
5 | import ViewportWidth from 'package/components/ViewportWidth'
6 | import ContainerWidth from 'package/components/ContainerWidth'
7 | import LessonAction from './components/LessonAction'
8 |
9 | const stateVerbs = keys(lessonStateVerbToPastTense)
10 |
11 | const LessonActions = ({
12 | lesson,
13 | requestLesson,
14 | requestCurrentPage,
15 | }) => {
16 |
17 | const getItems = (isLikelyDesktop) => compact([
18 |
19 | isLikelyDesktop && lesson.upload_lesson_http_url
20 | ?
29 | : null,
30 |
31 | lesson.edit_lesson_http_url
32 | ?
38 | : null,
39 |
40 | ...map(stateVerbs, (stateVerb, index) => {
41 | const stateVerbUrl = lesson[`${stateVerb}_url`]
42 | return stateVerbUrl
43 | ?
49 | {({request}) => {
50 | const details = detailsByLessonState[lessonStateVerbToPastTense[stateVerb]]
51 | return (
52 | request()}
57 | />
58 | )
59 | }}
60 |
61 | : null
62 | }),
63 | ])
64 |
65 | return (
66 |
67 | {(isLikelyDesktop) => (
68 |
69 | {(containerWidth) => (
70 |
77 | {map(getItems(isLikelyDesktop), (item, index) => (
78 |
94 | {item}
95 |
96 | ))}
97 |
98 | )}
99 |
100 | )}
101 |
102 | )
103 | }
104 |
105 | LessonActions.propTypes = {
106 | lesson: PropTypes.object.isRequired,
107 | requestLesson: PropTypes.func,
108 | requestCurrentPage: PropTypes.func,
109 | }
110 |
111 | export default LessonActions
112 |
--------------------------------------------------------------------------------
/src/package/index.js:
--------------------------------------------------------------------------------
1 | import AnchorSource from 'package/components/Anchor'
2 | import AvatarSource from 'package/components/Avatar'
3 | import ButtonSource from 'package/components/Button'
4 | import CardSource from 'package/components/Card'
5 | import ContainerWidthSource from 'package/components/ContainerWidth'
6 | import ErrorSource from 'package/components/Error'
7 | import HeaderCardSource from 'package/components/HeaderCard'
8 | import HeadingSource from 'package/components/Heading'
9 | import IconSource from 'package/components/Icon'
10 | import IconLabelSource from 'package/components/IconLabel'
11 | import ImageSource from 'package/components/Image'
12 | import InstructorLessonsSource from 'package/components/InstructorLessons'
13 | import InstructorRevenueSource from 'package/components/InstructorRevenue'
14 | import InstructorStatsSource from 'package/components/InstructorStats'
15 | import LayoutColumnsSource from 'package/components/LayoutColumns'
16 | import LessonActionsSource from 'package/components/LessonActions'
17 | import LessonOverviewsSource from 'package/components/LessonOverviews'
18 | import LessonOverviewsByGroupSource from 'package/components/LessonOverviewsByGroup'
19 | import ListSource from 'package/components/List'
20 | import LoadingSource from 'package/components/Loading'
21 | import MarkdownSource from 'package/components/Markdown'
22 | import MaybeSource from 'package/components/Maybe'
23 | import OpenSource from 'package/components/Open'
24 | import ParagraphSource from 'package/components/Paragraph'
25 | import PromptSource from 'package/components/Prompt'
26 | import RequestSource from 'package/components/Request'
27 | import RequestedLessonsSource from 'package/components/RequestedLessons'
28 | import TabsSource from 'package/components/Tabs'
29 | import ToggleSource from 'package/components/Toggle'
30 | import ViewportWidthSource from 'package/components/ViewportWidth'
31 |
32 | import InstructorDashboardSource from 'package/screens/InstructorDashboard'
33 | import InstructorDetailsSource from 'package/screens/InstructorDetails'
34 | import InstructorsDirectorySource from 'package/screens/InstructorsDirectory'
35 | import LessonDetailsSource from 'package/screens/LessonDetails'
36 | import LessonsDirectorySource from 'package/screens/LessonsDirectory'
37 | import NewLessonSource from 'package/screens/NewLesson'
38 |
39 | import colorsSource from 'package/utils/colors'
40 | import colorValuesSource from 'package/utils/colorValues'
41 | import loginSource from 'package/utils/login'
42 | import logoutSource from 'package/utils/logout'
43 |
44 | export const Anchor = AnchorSource
45 | export const Avatar = AvatarSource
46 | export const Button = ButtonSource
47 | export const Card = CardSource
48 | export const ContainerWidth = ContainerWidthSource
49 | export const Error = ErrorSource
50 | export const HeaderCard = HeaderCardSource
51 | export const Heading = HeadingSource
52 | export const Icon = IconSource
53 | export const IconLabel = IconLabelSource
54 | export const Image = ImageSource
55 | export const InstructorLessons = InstructorLessonsSource
56 | export const InstructorRevenue = InstructorRevenueSource
57 | export const InstructorStats = InstructorStatsSource
58 | export const LayoutColumns = LayoutColumnsSource
59 | export const LessonActions = LessonActionsSource
60 | export const LessonOverviews = LessonOverviewsSource
61 | export const LessonOverviewsByGroup = LessonOverviewsByGroupSource
62 | export const List = ListSource
63 | export const Loading = LoadingSource
64 | export const Markdown = MarkdownSource
65 | export const Maybe = MaybeSource
66 | export const Open = OpenSource
67 | export const Paragraph = ParagraphSource
68 | export const Prompt = PromptSource
69 | export const Request = RequestSource
70 | export const RequestedLessons = RequestedLessonsSource
71 | export const Tabs = TabsSource
72 | export const Toggle = ToggleSource
73 | export const ViewportWidth = ViewportWidthSource
74 |
75 | export const InstructorDashboard = InstructorDashboardSource
76 | export const NewLesson = NewLessonSource
77 | export const LessonDetails = LessonDetailsSource
78 | export const LessonsDirectory = LessonsDirectorySource
79 | export const InstructorDetails = InstructorDetailsSource
80 | export const InstructorsDirectory = InstructorsDirectorySource
81 |
82 | export const colors = colorsSource
83 | export const colorValues = colorValuesSource
84 | export const login = loginSource
85 | export const logout = logoutSource
86 |
--------------------------------------------------------------------------------
/src/package/components/RequestedLessons/components/ProposeLesson/components/ProposeLessonForm/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {map, size, every} from 'lodash'
3 | import Maybe from 'package/components/Maybe'
4 | import Error from 'package/components/Error'
5 | import Button from 'package/components/Button'
6 | import Paragraph from 'package/components/Paragraph'
7 | import Request from 'package/components/Request'
8 |
9 | const inputClassNames = 'input-reset pa2 br2 ba b--gray-secondary dark-gray w-100'
10 |
11 | const clearedState = {
12 | title: '',
13 | technologyId: '',
14 | summary: '',
15 | hasMissingInput: false,
16 | }
17 |
18 | export default class extends Component {
19 |
20 | state = clearedState
21 |
22 | handleMissingInput = () => {
23 | this.setState({hasMissingInput: true})
24 | }
25 |
26 | handleTitleChange = (event) => {
27 | this.setState({
28 | title: event.target.value
29 | })
30 | }
31 |
32 | handleTechnologyChange = (event) => {
33 | this.setState({
34 | technologyId: event.target.value
35 | })
36 | }
37 |
38 | handleSummaryChange = (event) => {
39 | this.setState({
40 | summary: event.target.value
41 | })
42 | }
43 |
44 | handleResponse = (response) => {
45 | if(!response) {
46 | this.setState(clearedState)
47 | }
48 | }
49 |
50 | render() {
51 | const {
52 | title,
53 | technologyId,
54 | summary,
55 | hasMissingInput,
56 | } = this.state
57 | const {instructor, lessonsUrl, technologiesUrl} = this.props
58 |
59 | return (
60 |
61 |
62 |
63 | Have an idea for an egghead lesson? Fill out this information and get started. Feel free to submit as many ideas as you like.
64 |
65 |
66 |
67 |
68 | Title *
69 |
70 |
76 |
77 |
78 |
79 | {({data}) => (
80 |
81 |
82 | Technology *
83 |
84 |
89 |
90 | {map(data.technologies, (technology) => (
91 |
95 | {technology.label}
96 |
97 | ))}
98 |
99 |
100 | )}
101 |
102 |
103 |
104 |
105 | Summary
106 |
107 |
114 |
115 |
116 |
117 |
118 |
119 | Missing required form input
120 |
121 |
122 |
123 |
124 |
138 | {({request}) => (
139 | {
142 | if(every([title, technologyId], (input) => size(input) > 0)) {
143 | request()
144 | }
145 | else {
146 | this.handleMissingInput()
147 | }
148 | }}
149 | >
150 | Submit
151 |
152 | )}
153 |
154 |
155 |
156 | )
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/package/components/InstructorRevenue/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {find, size, map, includes} from 'lodash'
3 | import colorValues from 'package/utils/colorValues'
4 | import Card from 'package/components/Card'
5 | import Request from 'package/components/Request'
6 | import Open from 'package/components/Open'
7 | import ContainerWidth from 'package/components/ContainerWidth'
8 | import currentMonthStartDate from './utils/currentMonthStartDate'
9 | import totalRevenue from './utils/totalRevenue'
10 | import removeRevenueMonth from './utils/removeRevenueMonth'
11 | import prettyMonthName from './utils/prettyMonthName'
12 | import RevenuePeriod from './components/RevenuePeriod'
13 | import LineChart from './components/LineChart'
14 |
15 | const revenueColor = colorValues['blue']
16 | const minutesColor = colorValues['dark-gray-secondary']
17 | const activeLabelClassName = 'dark-gray b'
18 |
19 | const InstructorRevenue = ({revenueUrl}) => revenueUrl
20 | ?
21 | {({data}) => {
22 | const currentMonthRevenue = find(data, ['month', currentMonthStartDate()])
23 | const currentTotalRevenue = totalRevenue(removeRevenueMonth(data, currentMonthStartDate()))
24 | const hasCurrentRevenue = size(data) > 0
25 | && ((currentMonthRevenue && currentMonthRevenue.revenue > 0) || (currentTotalRevenue && currentTotalRevenue.revenue > 0))
26 |
27 | if(!hasCurrentRevenue) {
28 | return null
29 | }
30 |
31 | const currentMonthNames = map(data, month => prettyMonthName(month.month))
32 | const currentRevenuePoints = map(data, month => month.revenue)
33 | const currentMinutesPoints = map(data, month => month.minutes_watched)
34 |
35 | return (
36 |
37 | {({isOpen, handleOpenToggleClick}) => (
38 |
39 | {(containerWidth) => {
40 | const shouldStack = includes(['xsmall', 'small'], containerWidth)
41 | return (
42 |
43 |
47 |
48 |
56 |
57 |
58 |
63 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
75 |
79 |
80 | Revenue
81 |
82 |
83 | /
84 |
85 |
86 | Minutes
87 |
88 |
89 |
90 | {isOpen
91 | ?
101 | :
112 | }
113 |
114 |
115 |
116 |
117 |
118 | )
119 | }}
120 |
121 | )}
122 |
123 | )
124 | }}
125 |
126 | : null
127 |
128 | InstructorRevenue.propTypes = {
129 | revenueUrl: PropTypes.string,
130 | }
131 |
132 | export default InstructorRevenue
133 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Background
4 |
5 | - The **app** uses `react-scripts` from Create React App
6 | - The **package** is an npm package
7 |
8 | ## Workflow
9 |
10 | - Ensure you have Git, Node, and Yarn installed
11 | - Clone the `egghead-ui` repo and `cd` into it
12 | - Create a feature branch off of `master`
13 | - `yarn` to install latest dependencies
14 | - `yarn dev:package` to develop the **package**
15 | - Use [`yarn link`](https://yarnpkg.com/lang/en/docs/cli/link/) to test using the **package** in another project
16 | - `yarn dev:app` to develop the **app** with local dev endpoints ([egghead-rails](https://github.com/eggheadio/egghead-rails) needs to be running locally)
17 | - [localhost:2000](http://localhost:2000) to view the **app**
18 | - `yarn dev:app:staging` to develop the **app** with staging endpoints
19 | - `yarn test` to run tests
20 | - `yarn lint` to run linting
21 | - Ensure any changed functionality in `src/package/*` is updated in the related `src/App/utils/resourcesByTypes` file(s), using faker where possible
22 | - Stage, commit, and push
23 | - If you want to publish a new **package** release, run `yarn bump` and type in the new version using [Semantic Versioning](http://semver.org/); this will bump the `package.json` version and push a new git tag
24 | - Submit a pull request for the feature branch to `master`
25 | - Once PR status passes (approved review and successful [continuous integration in Travis CI](https://travis-ci.org/eggheadio/egghead-ui), merge the pull request
26 | - When `master` is updated
27 | - [Continuous deployment runs in Travis CI](https://travis-ci.org/eggheadio/egghead-ui) and deploys the latest **app** to [styleguide.egghead.io](https://styleguide.egghead.io)
28 | - If there was a version bump (`package.json` version and new git tag), it also publishes the new **package** version to [npm](https://www.npmjs.com/package/egghead-ui)
29 | - Notify consuming projects to run `yarn upgrade egghead-ui` in their projects to get latest, with a list of changes
30 |
31 | ## Folders and files
32 |
33 | - `README.md`: user documentation
34 | - `CONTRIBUTING.md`: contributor documentation
35 | - `package.json`: npm data, package configuration, and scripts
36 | - `yarn.lock`: specific dependency information for consistent `yarn` installs
37 | - `.travis.yml`: configuration for continuous integration and deployment
38 | - `static.json`: used by Heroku to deploy a static web app
39 | - `src/`: source code
40 | - `package/`: **package** resources
41 | - `index.js`: exports for npm
42 | - `components/{ComponentName}`: **package** component
43 | - `index.js`: defines and exports the component
44 | - `components/`: contains sub-components used by the component
45 | - `utils/`: modules used by the component
46 | - `utils/`: modules shared across multiple components
47 | - `index.js` wires up the **app**
48 | - `App/`: **app** component
49 | - `public`: required by create-react-app to create the **app** build
50 | - `lib`: generated for **package** installs on npm
51 | - `build`: generated for a deployable version of the **app**
52 |
53 | ## Conventions
54 |
55 | ### Package management
56 |
57 | Yarn is used for package management. Use the [yarn cli](https://yarnpkg.com/en/docs/usage) to add/remove/update packages which updates both the `package.json` and `yarn.lock` to ensure consistent package installs.
58 |
59 | ### Scripts
60 |
61 | Yarn is used for running scripts. Use `yarn {script}` to run them. The core of the scripts extend `react-scripts` (from Create React App) so that this project doesn't have to manage compiling, linting, or testing; it generally gets all [Create React App updates](https://github.com/facebookincubator/create-react-app/releases) for free by running `yarn upgrade react-scripts`. Avoid ejecting the project for this reason if at all possible.
62 |
63 | ### Components
64 |
65 | `src` is made up primarily of **components**. A component is a directory organized _by feature_. They look like this:
66 |
67 | ```
68 | SomeComponentName/
69 | index.js (entry point)
70 | components/ (optional sub-components)
71 | utils/ (optional utility modules)
72 | ```
73 |
74 | ### Screens
75 |
76 | Some components are also **screens**. A screen is a collection of components that form a "page"; it is generally a collection of `Card`s. They look like this:
77 |
78 | ```
79 | SomeScreenName/
80 | index.js (entry point)
81 | screens/ (optional sub-screens paired with sub-routes)
82 | components/ (optional sub-components)
83 | utils/ (optional utility modules)
84 | ```
85 |
86 | ### Endpoints
87 |
88 | The `Request` component is used for making requests to endpoints. Hypermedia properties (`*_url`) should be used for all URLs in `package/*` resources; this ensures endpoint permissions and environment.
89 |
90 | ### Paths
91 |
92 | ES2015 modules are used for sharing code between files. Both `NODE_PATH` (for the **app**) and `babel-plugin-module-resolver` (for the **package**) are set to `src` so `import Icon from 'package/components/Icon'` will grab `src/package/components/Icon`. When trying to decide if an import should use an absolute or relative path, it depends on the situation: if something belongs to an inner module/component, it should reference the pieces relatively; if something is using a general promoted module/component, it should import the pieces absolutely. A good rule of thumb is to keep everything relative that would be moved together so it is self-contained.
93 |
94 | ### Promotion
95 |
96 | All resources are eligible for **promotion** to facilitate code reuse. If a resource is shared by multiple files the principle of [lowest common ancestor](https://en.wikipedia.org/wiki/Lowest_common_ancestor) applies and that shared resource is _promoted_ to the lowest common ancestor’s directory.
97 |
98 | ### JavaScript
99 |
100 | Latest JS syntax is used where it makes sense. [Lodash](lodash.com) methods are often used in place of native methods for performance, convenience, and consistency.
101 |
102 | ### Styles
103 |
104 | The root egghead-ui components are used wherever possible. Styling is largely taken care of by these components. When app specific styles are needed beyond that, `tachyons-egghead` classes are used where possible. Styles are added to work on mobile first based on the container size (using flex wrap with flex values or max width, or ``). When more customization is needed, `styled-components` is used.
105 |
106 | ### Linting
107 |
108 | Linting is currently provided by `react-scripts` (ESLint).
109 |
110 | ### Testing
111 |
112 | Testing is currently provided by `react-scripts` (Jest). Files that could benefit from tests have an `index.test.js` file next to them. These are generally simple unit or snapshot tests where they provide value.
113 |
--------------------------------------------------------------------------------
/src/App/components/Resource/components/Examples/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {map, includes, size, uniqueId} from 'lodash'
3 | import {
4 | xsmallContainerWidth,
5 | smallContainerWidth,
6 | mediumContainerWidth,
7 | largeContainerWidth,
8 | xlargeContainerWidth,
9 | xxlargeContainerWidth,
10 | } from 'package/utils/hardCodedSizes'
11 | import Heading from 'package/components/Heading'
12 | import Button from 'package/components/Button'
13 |
14 | const containerWidthActions = [
15 | {
16 | label: 'XSmall',
17 | containerWidth: xsmallContainerWidth,
18 | },
19 | {
20 | label: 'Small',
21 | containerWidth: smallContainerWidth,
22 | },
23 | {
24 | label: 'Medium',
25 | containerWidth: mediumContainerWidth,
26 | },
27 | {
28 | label: 'Large',
29 | containerWidth: largeContainerWidth,
30 | },
31 | {
32 | label: 'XLarge',
33 | containerWidth: xlargeContainerWidth,
34 | },
35 | {
36 | label: 'XXLarge',
37 | containerWidth: xxlargeContainerWidth,
38 | },
39 | ]
40 |
41 | const containerBackgroundActions = [
42 | {
43 | label: 'Light',
44 | containerBackground: 'light',
45 | },
46 | {
47 | label: 'Dark',
48 | containerBackground: 'dark',
49 | },
50 | ]
51 |
52 | const containerPaddingActions = [
53 | {
54 | label: 'On',
55 | containerPadding: true,
56 | },
57 | {
58 | label: 'Off',
59 | containerPadding: false,
60 | },
61 | ]
62 |
63 | const containerBoxSizing = 8
64 |
65 | export const optOuts = [
66 | 'containerWidth',
67 | 'containerBackground',
68 | 'containerPadding',
69 | 'types',
70 | ]
71 |
72 | class Examples extends Component {
73 |
74 | state = {
75 | containerWidth: xsmallContainerWidth,
76 | containerBackground: 'light',
77 | containerPadding: true,
78 | examples: false,
79 | }
80 |
81 | componentWillMount = () => {
82 | this.handleCreateExamples()
83 | }
84 |
85 | handleContainerWidthChange = (containerWidth) => {
86 | this.setState({
87 | containerWidth,
88 | })
89 | }
90 |
91 | handleBackgroundChange = (containerBackground) => {
92 | this.setState({
93 | containerBackground,
94 | })
95 | }
96 |
97 | handlePaddingChange = (containerPadding) => {
98 | this.setState({
99 | containerPadding,
100 | })
101 | }
102 |
103 | handleCreateExamples = () => {
104 | this.setState({
105 | examples: this.props.createExamples(),
106 | renderId: uniqueId(),
107 | })
108 | }
109 |
110 | render() {
111 | const {
112 | containerWidth,
113 | containerBackground,
114 | containerPadding,
115 | examples,
116 | renderId,
117 | } = this.state
118 | const {optOut} = this.props
119 |
120 | return (
121 |
122 |
123 | {size(optOut) < size(optOuts)
124 | ?
132 |
133 | {includes(optOut, 'types')
134 | ? null
135 | :
136 |
137 | Types
138 |
139 |
144 | Randomize
145 |
146 |
147 | }
148 |
149 | {includes(optOut, 'containerWidth')
150 | ? null
151 | :
152 |
153 | Container Width
154 |
155 |
156 | {map(containerWidthActions, action => (
157 |
161 |
166 | {action.label}
167 |
168 |
169 | ))}
170 |
171 |
172 | }
173 |
174 | {includes(optOut, 'containerBackground')
175 | ? null
176 | :
177 |
178 | Container Background
179 |
180 |
181 | {map(containerBackgroundActions, action => (
182 |
186 |
191 | {action.label}
192 |
193 |
194 | ))}
195 |
196 |
197 | }
198 |
199 | {includes(optOut, 'containerPadding')
200 | ? null
201 | :
202 |
203 | Container Padding
204 |
205 |
206 | {map(containerPaddingActions, action => (
207 |
211 |
216 | {action.label}
217 |
218 |
219 | ))}
220 |
221 |
222 | }
223 |
224 |
225 | : null
226 | }
227 |
228 |
229 | {map(examples, (example, index) => (
230 |
234 |
245 | {example}
246 |
247 |
248 | ))}
249 |
250 |
251 |
252 | )
253 | }
254 | }
255 |
256 | export default Examples
257 |
--------------------------------------------------------------------------------
/src/package/components/Icon/index.js:
--------------------------------------------------------------------------------
1 | import React, {PropTypes} from 'react'
2 | import {keys} from 'lodash'
3 | import {
4 | EggheadAccept,
5 | EggheadAlert,
6 | EggheadArrowDown,
7 | EggheadArrowLeft,
8 | EggheadArrowRight,
9 | EggheadArrowUp,
10 | EggheadCheck,
11 | EggheadCheckWatched,
12 | EggheadCircleArrowDownOutline,
13 | EggheadClockTime,
14 | EggheadCommentAlert,
15 | EggheadCross,
16 | EggheadEdit,
17 | EggheadEnterSearch,
18 | EggheadFacebook,
19 | EggheadFlag,
20 | EggheadFolderLanguage,
21 | EggheadFullCoursesHeadline,
22 | EggheadHeart,
23 | EggheadHelp,
24 | EggheadHistory,
25 | EggheadInfo,
26 | EggheadLessonPlayPlaylist,
27 | EggheadMail,
28 | EggheadPlay,
29 | EggheadPlaybook,
30 | EggheadPlayCourse,
31 | EggheadPlayLesson,
32 | EggheadPlus,
33 | EggheadProfile,
34 | EggheadProgressRing,
35 | EggheadQuestion,
36 | EggheadQuickLessonsHeadline,
37 | EggheadRefresh,
38 | EggheadRequest,
39 | EggheadRevise,
40 | EggheadRss,
41 | EggheadSignOut,
42 | EggheadStarFill,
43 | EggheadStarOutline,
44 | EggheadStats,
45 | EggheadTechnology,
46 | EggheadThumbsDown,
47 | EggheadThumbsUp,
48 | EggheadTimerPlus,
49 | EggheadTwitter,
50 | EggheadUpdate,
51 | EggheadUpload,
52 | EggheadUploadCloud,
53 | } from 'react-icons/lib/egghead'
54 | import {
55 | FaAdjust,
56 | FaAlignCenter,
57 | FaAlignJustify,
58 | FaAlignLeft,
59 | FaAlignRight,
60 | FaAngleDoubleDown,
61 | FaAngleDoubleLeft,
62 | FaAngleDoubleRight,
63 | FaAngleDoubleUp,
64 | FaAngleDown,
65 | FaAngleLeft,
66 | FaAngleRight,
67 | FaAngleUp,
68 | FaApple,
69 | FaArchive,
70 | FaAreaChart,
71 | FaArrowCircleDown,
72 | FaArrowCircleLeft,
73 | FaArrowCircleODown,
74 | FaArrowCircleOLeft,
75 | FaArrowCircleORight,
76 | FaArrowCircleOUp,
77 | FaArrowCircleRight,
78 | FaArrowCircleUp,
79 | FaArrows,
80 | FaArrowsAlt,
81 | FaArrowsH,
82 | FaArrowsV,
83 | FaAsterisk,
84 | FaAt,
85 | FaBackward,
86 | FaBan,
87 | FaBank,
88 | FaBarChart,
89 | FaBarcode,
90 | FaBars,
91 | FaBirthdayCake,
92 | FaBomb,
93 | FaBook,
94 | FaBookmark,
95 | FaBookmarkO,
96 | FaBug,
97 | FaBullhorn,
98 | FaBullseye,
99 | FaCalculator,
100 | FaCalendar,
101 | FaCalendarCheckO,
102 | FaCalendarMinusO,
103 | FaCalendarO,
104 | FaCalendarPlusO,
105 | FaCalendarTimesO,
106 | FaCamera,
107 | FaCameraRetro,
108 | FaCaretDown,
109 | FaCaretLeft,
110 | FaCaretRight,
111 | FaCaretSquareODown,
112 | FaCaretSquareOLeft,
113 | FaCaretSquareORight,
114 | FaCaretSquareOUp,
115 | FaCaretUp,
116 | FaCartArrowDown,
117 | FaCartPlus,
118 | FaCertificate,
119 | FaChain,
120 | FaChainBroken,
121 | FaCheckCircle,
122 | FaCheckCircleO,
123 | FaCheckSquare,
124 | FaCheckSquareO,
125 | FaChevronCircleDown,
126 | FaChevronCircleLeft,
127 | FaChevronCircleRight,
128 | FaChevronCircleUp,
129 | FaChevronDown,
130 | FaChevronLeft,
131 | FaChevronRight,
132 | FaChevronUp,
133 | FaChrome,
134 | FaCircle,
135 | FaCircleO,
136 | FaCircleONotch,
137 | FaCircleThin,
138 | FaClipboard,
139 | FaClockO,
140 | FaClone,
141 | FaClose,
142 | FaCloud,
143 | FaCloudDownload,
144 | FaCloudUpload,
145 | FaCode,
146 | FaCodeFork,
147 | FaCodepen,
148 | FaCoffee,
149 | FaCog,
150 | FaCogs,
151 | FaColumns,
152 | FaComment,
153 | FaCommentO,
154 | FaCommenting,
155 | FaCommentingO,
156 | FaComments,
157 | FaCommentsO,
158 | FaCompress,
159 | FaCopy,
160 | FaCopyright,
161 | FaCreativeCommons,
162 | FaCreditCard,
163 | FaCreditCardAlt,
164 | FaCrop,
165 | FaCrosshairs,
166 | FaCss3,
167 | FaCube,
168 | FaCubes,
169 | FaCut,
170 | FaDashboard,
171 | FaDatabase,
172 | FaDedent,
173 | FaDesktop,
174 | FaDiamond,
175 | FaDotCircleO,
176 | FaDownload,
177 | FaEject,
178 | FaEllipsisH,
179 | FaEllipsisV,
180 | FaEnvelope,
181 | FaEnvelopeO,
182 | FaEnvelopeSquare,
183 | FaEraser,
184 | FaExchange,
185 | FaExclamation,
186 | FaExclamationCircle,
187 | FaExclamationTriangle,
188 | FaExpand,
189 | FaExternalLink,
190 | FaExternalLinkSquare,
191 | FaEye,
192 | FaEyeSlash,
193 | FaEyedropper,
194 | FaFastBackward,
195 | FaFastForward,
196 | FaFeed,
197 | FaFile,
198 | FaFileArchiveO,
199 | FaFileAudioO,
200 | FaFileCodeO,
201 | FaFileExcelO,
202 | FaFileImageO,
203 | FaFileMovieO,
204 | FaFileO,
205 | FaFilePdfO,
206 | FaFilePowerpointO,
207 | FaFileText,
208 | FaFileTextO,
209 | FaFileWordO,
210 | FaFilm,
211 | FaFilter,
212 | FaFlagCheckered,
213 | FaFlagO,
214 | FaFlask,
215 | FaFloppyO,
216 | FaFolder,
217 | FaFolderO,
218 | FaFolderOpen,
219 | FaFolderOpenO,
220 | FaFont,
221 | FaForward,
222 | FaFrownO,
223 | FaFutbolO,
224 | FaGavel,
225 | FaGift,
226 | FaGit,
227 | FaGitSquare,
228 | FaGithub,
229 | FaGithubAlt,
230 | FaGlass,
231 | FaGlobe,
232 | FaGoogle,
233 | FaGooglePlus,
234 | FaGooglePlusSquare,
235 | FaGraduationCap,
236 | FaGroup,
237 | FaHandGrabO,
238 | FaHandODown,
239 | FaHandOLeft,
240 | FaHandORight,
241 | FaHandOUp,
242 | FaHashtag,
243 | FaHddO,
244 | FaHome,
245 | FaHourglass,
246 | FaHourglass1,
247 | FaHourglass2,
248 | FaHourglass3,
249 | FaHourglassO,
250 | FaHtml5,
251 | FaICursor,
252 | FaImage,
253 | FaInbox,
254 | FaIndent,
255 | FaInfoCircle,
256 | FaInstagram,
257 | FaItalic,
258 | FaJsfiddle,
259 | FaKey,
260 | FaKeyboardO,
261 | FaLaptop,
262 | FaLeaf,
263 | FaLevelDown,
264 | FaLevelUp,
265 | FaLightbulbO,
266 | FaLineChart,
267 | FaList,
268 | FaListAlt,
269 | FaListOl,
270 | FaListUl,
271 | FaLocationArrow,
272 | FaLock,
273 | FaLongArrowDown,
274 | FaLongArrowLeft,
275 | FaLongArrowRight,
276 | FaLongArrowUp,
277 | FaMagic,
278 | FaMagnet,
279 | FaMailForward,
280 | FaMailReply,
281 | FaMailReplyAll,
282 | FaMap,
283 | FaMapPin,
284 | FaMapSigns,
285 | FaMehO,
286 | FaMicrophone,
287 | FaMicrophoneSlash,
288 | FaMinus,
289 | FaMinusCircle,
290 | FaMinusSquare,
291 | FaMinusSquareO,
292 | FaMobile,
293 | FaMoney,
294 | FaMoonO,
295 | FaMousePointer,
296 | FaMusic,
297 | FaNewspaperO,
298 | FaObjectGroup,
299 | FaObjectUngroup,
300 | FaPaperPlane,
301 | FaPaperPlaneO,
302 | FaPaperclip,
303 | FaParagraph,
304 | FaPause,
305 | FaPauseCircle,
306 | FaPauseCircleO,
307 | FaPencil,
308 | FaPencilSquare,
309 | FaPercent,
310 | FaPhone,
311 | FaPhoneSquare,
312 | FaPieChart,
313 | FaPlug,
314 | FaPlusCircle,
315 | FaPlusSquare,
316 | FaPlusSquareO,
317 | FaPrint,
318 | FaQuestionCircle,
319 | FaQuestionCircleO,
320 | FaQuoteLeft,
321 | FaQuoteRight,
322 | FaRecycle,
323 | FaRegistered,
324 | FaRepeat,
325 | FaRetweet,
326 | FaRotateLeft,
327 | FaRssSquare,
328 | FaSearch,
329 | FaSearchMinus,
330 | FaSearchPlus,
331 | FaServer,
332 | FaShareAlt,
333 | FaShareAltSquare,
334 | FaShareSquare,
335 | FaShareSquareO,
336 | FaShoppingCart,
337 | FaSignIn,
338 | FaSignal,
339 | FaSitemap,
340 | FaSlack,
341 | FaSliders,
342 | FaSmileO,
343 | FaSort,
344 | FaSortAlphaAsc,
345 | FaSortAlphaDesc,
346 | FaSortAmountAsc,
347 | FaSortAmountDesc,
348 | FaSortAsc,
349 | FaSortDesc,
350 | FaSortNumericAsc,
351 | FaSortNumericDesc,
352 | FaSpinner,
353 | FaSquare,
354 | FaSquareO,
355 | FaStar,
356 | FaStarHalf,
357 | FaStarHalfEmpty,
358 | FaStarO,
359 | FaStepBackward,
360 | FaStepForward,
361 | FaStickyNote,
362 | FaStickyNoteO,
363 | FaStop,
364 | FaStopCircle,
365 | FaStopCircleO,
366 | FaSunO,
367 | FaTable,
368 | FaTablet,
369 | FaTag,
370 | FaTags,
371 | FaTasks,
372 | FaTelevision,
373 | FaTerminal,
374 | FaTextHeight,
375 | FaTextWidth,
376 | FaTh,
377 | FaThLarge,
378 | FaThList,
379 | FaThumbTack,
380 | FaThumbsODown,
381 | FaThumbsOUp,
382 | FaTicket,
383 | FaTimesCircle,
384 | FaTimesCircleO,
385 | FaToggleOff,
386 | FaToggleOn,
387 | FaTrademark,
388 | FaTrash,
389 | FaTrashO,
390 | FaTree,
391 | FaTrophy,
392 | FaTwitterSquare,
393 | FaUnderline,
394 | FaUniversalAccess,
395 | FaUnlock,
396 | FaUnlockAlt,
397 | FaUsb,
398 | FaUser,
399 | FaUserPlus,
400 | FaUserTimes,
401 | FaVideoCamera,
402 | FaVimeoSquare,
403 | FaVolumeControlPhone,
404 | FaVolumeDown,
405 | FaVolumeOff,
406 | FaVolumeUp,
407 | FaWheelchair,
408 | FaWheelchairAlt,
409 | FaWifi,
410 | FaWindows,
411 | FaWrench,
412 | } from 'react-icons/lib/fa'
413 | import colors from 'package/utils/colors'
414 |
415 | const typeToSvgIcon = {
416 | 'accept': EggheadAccept,
417 | 'add': EggheadPlus,
418 | 'adjust': FaAdjust,
419 | 'alert': EggheadAlert,
420 | 'align-center': FaAlignCenter,
421 | 'align-justify': FaAlignJustify,
422 | 'align-left': FaAlignLeft,
423 | 'align-right': FaAlignRight,
424 | 'angle-double-down': FaAngleDoubleDown,
425 | 'angle-double-left': FaAngleDoubleLeft,
426 | 'angle-double-right': FaAngleDoubleRight,
427 | 'angle-double-up': FaAngleDoubleUp,
428 | 'angle-down': FaAngleDown,
429 | 'angle-left': FaAngleLeft,
430 | 'angle-right': FaAngleRight,
431 | 'angle-up': FaAngleUp,
432 | 'apple': FaApple,
433 | 'archive': FaArchive,
434 | 'area-chart': FaAreaChart,
435 | 'arrow-circle-down': FaArrowCircleDown,
436 | 'arrow-circle-left': FaArrowCircleLeft,
437 | 'arrow-circle-o-down': FaArrowCircleODown,
438 | 'arrow-circle-o-left': FaArrowCircleOLeft,
439 | 'arrow-circle-o-right': FaArrowCircleORight,
440 | 'arrow-circle-o-up': FaArrowCircleOUp,
441 | 'arrow-circle-right': FaArrowCircleRight,
442 | 'arrow-circle-up': FaArrowCircleUp,
443 | 'arrow-down': EggheadArrowDown,
444 | 'arrow-left': EggheadArrowLeft,
445 | 'arrow-right': EggheadArrowRight,
446 | 'arrow-up': EggheadArrowUp,
447 | 'arrows': FaArrows,
448 | 'arrows-alt': FaArrowsAlt,
449 | 'arrows-h': FaArrowsH,
450 | 'arrows-v': FaArrowsV,
451 | 'asterisk': FaAsterisk,
452 | 'at': FaAt,
453 | 'backward': FaBackward,
454 | 'ban': FaBan,
455 | 'bank': FaBank,
456 | 'bar-chart': FaBarChart,
457 | 'barcode': FaBarcode,
458 | 'bars': FaBars,
459 | 'birthday-cake': FaBirthdayCake,
460 | 'bomb': FaBomb,
461 | 'book': FaBook,
462 | 'bookmark': FaBookmark,
463 | 'bookmark-o': FaBookmarkO,
464 | 'box': FaSquareO,
465 | 'box-check': FaCheckSquareO,
466 | 'bug': FaBug,
467 | 'bullhorn': FaBullhorn,
468 | 'bullseye': FaBullseye,
469 | 'calculator': FaCalculator,
470 | 'calendar': FaCalendar,
471 | 'calendar-check-o': FaCalendarCheckO,
472 | 'calendar-minus-o': FaCalendarMinusO,
473 | 'calendar-o': FaCalendarO,
474 | 'calendar-plus-o': FaCalendarPlusO,
475 | 'calendar-times-o': FaCalendarTimesO,
476 | 'camera': FaCamera,
477 | 'camera-retro': FaCameraRetro,
478 | 'cancel': EggheadCross,
479 | 'caret-down': FaCaretDown,
480 | 'caret-left': FaCaretLeft,
481 | 'caret-right': FaCaretRight,
482 | 'caret-square-o-down': FaCaretSquareODown,
483 | 'caret-square-o-left': FaCaretSquareOLeft,
484 | 'caret-square-o-right': FaCaretSquareORight,
485 | 'caret-square-o-up': FaCaretSquareOUp,
486 | 'caret-up': FaCaretUp,
487 | 'cart-arrow-down': FaCartArrowDown,
488 | 'cart-plus': FaCartPlus,
489 | 'certificate': FaCertificate,
490 | 'chain': FaChain,
491 | 'chain-broken': FaChainBroken,
492 | 'check': EggheadCheck,
493 | 'check-circle': FaCheckCircle,
494 | 'check-circle-o': FaCheckCircleO,
495 | 'check-square': FaCheckSquare,
496 | 'check-square-o': FaCheckSquareO,
497 | 'check-watched': EggheadCheckWatched,
498 | 'chevron-circle-down': FaChevronCircleDown,
499 | 'chevron-circle-left': FaChevronCircleLeft,
500 | 'chevron-circle-right': FaChevronCircleRight,
501 | 'chevron-circle-up': FaChevronCircleUp,
502 | 'chevron-down': FaChevronDown,
503 | 'chevron-left': FaChevronLeft,
504 | 'chevron-right': FaChevronRight,
505 | 'chevron-up': FaChevronUp,
506 | 'chrome': FaChrome,
507 | 'circle': FaCircle,
508 | 'circle-arrow-down-outline': EggheadCircleArrowDownOutline,
509 | 'circle-o': FaCircleO,
510 | 'circle-o-notch': FaCircleONotch,
511 | 'circle-thin': FaCircleThin,
512 | 'clipboard': FaClipboard,
513 | 'clock': FaClockO,
514 | 'clock-time': EggheadClockTime,
515 | 'clone': FaClone,
516 | 'close': FaClose,
517 | 'cloud': FaCloud,
518 | 'cloud-download': FaCloudDownload,
519 | 'cloud-upload': FaCloudUpload,
520 | 'code': FaCode,
521 | 'code-fork': FaCodeFork,
522 | 'codepen': FaCodepen,
523 | 'coffee': FaCoffee,
524 | 'cog': FaCog,
525 | 'cogs': FaCogs,
526 | 'columns': FaColumns,
527 | 'comment': FaComment,
528 | 'comment-alert': EggheadCommentAlert,
529 | 'comment-o': FaCommentO,
530 | 'commenting': FaCommenting,
531 | 'commenting-o': FaCommentingO,
532 | 'comments': FaComments,
533 | 'comments-o': FaCommentsO,
534 | 'compress': FaCompress,
535 | 'copy': FaCopy,
536 | 'copyright': FaCopyright,
537 | 'course': FaFolderOpenO,
538 | 'course-headline': EggheadFullCoursesHeadline,
539 | 'creative-commons': FaCreativeCommons,
540 | 'credit-card': FaCreditCard,
541 | 'credit-card-alt': FaCreditCardAlt,
542 | 'crop': FaCrop,
543 | 'crosshairs': FaCrosshairs,
544 | 'css3': FaCss3,
545 | 'cube': FaCube,
546 | 'cubes': FaCubes,
547 | 'cut': FaCut,
548 | 'dashboard': FaDashboard,
549 | 'database': FaDatabase,
550 | 'dedent': FaDedent,
551 | 'desktop': FaDesktop,
552 | 'diamond': FaDiamond,
553 | 'dollar': FaMoney,
554 | 'dot-circle-o': FaDotCircleO,
555 | 'download': FaDownload,
556 | 'edit': EggheadEdit,
557 | 'eject': FaEject,
558 | 'ellipsis-h': FaEllipsisH,
559 | 'ellipsis-v': FaEllipsisV,
560 | 'enter': EggheadEnterSearch,
561 | 'envelope': FaEnvelope,
562 | 'envelope-o': FaEnvelopeO,
563 | 'envelope-square': FaEnvelopeSquare,
564 | 'eraser': FaEraser,
565 | 'exchange': FaExchange,
566 | 'exclamation': FaExclamation,
567 | 'exclamation-circle': FaExclamationCircle,
568 | 'exclamation-triangle': FaExclamationTriangle,
569 | 'expand': FaExpand,
570 | 'external-link': FaExternalLink,
571 | 'external-link-square': FaExternalLinkSquare,
572 | 'eye': FaEye,
573 | 'eye-slash': FaEyeSlash,
574 | 'eyedropper': FaEyedropper,
575 | 'facebook': EggheadFacebook,
576 | 'fast-backward': FaFastBackward,
577 | 'fast-forward': FaFastForward,
578 | 'feed': FaFeed,
579 | 'file': FaFile,
580 | 'file-archive-o': FaFileArchiveO,
581 | 'file-audio-o': FaFileAudioO,
582 | 'file-code-o': FaFileCodeO,
583 | 'file-excel-o': FaFileExcelO,
584 | 'file-image-o': FaFileImageO,
585 | 'file-movie-o': FaFileMovieO,
586 | 'file-o': FaFileO,
587 | 'file-pdf-o': FaFilePdfO,
588 | 'file-powerpoint-o': FaFilePowerpointO,
589 | 'file-text': FaFileText,
590 | 'file-text-o': FaFileTextO,
591 | 'file-word-o': FaFileWordO,
592 | 'film': FaFilm,
593 | 'filter': FaFilter,
594 | 'flag': EggheadFlag,
595 | 'flag-checkered': FaFlagCheckered,
596 | 'flag-o': FaFlagO,
597 | 'flask': FaFlask,
598 | 'floppy-o': FaFloppyO,
599 | 'folder': FaFolder,
600 | 'folder-language': EggheadFolderLanguage,
601 | 'folder-o': FaFolderO,
602 | 'folder-open': FaFolderOpen,
603 | 'folder-open-o': FaFolderOpenO,
604 | 'font': FaFont,
605 | 'forward': FaForward,
606 | 'frown-o': FaFrownO,
607 | 'futbol-o': FaFutbolO,
608 | 'gavel': FaGavel,
609 | 'gift': FaGift,
610 | 'git': FaGit,
611 | 'git-square': FaGitSquare,
612 | 'github': FaGithub,
613 | 'github-alt': FaGithubAlt,
614 | 'glass': FaGlass,
615 | 'globe': FaGlobe,
616 | 'google': FaGoogle,
617 | 'googlePlus': FaGooglePlus,
618 | 'googlePlusSquare': FaGooglePlusSquare,
619 | 'graduationCap': FaGraduationCap,
620 | 'group': FaGroup,
621 | 'hand-grab-o': FaHandGrabO,
622 | 'hand-o-down': FaHandODown,
623 | 'hand-o-left': FaHandOLeft,
624 | 'hand-o-right': FaHandORight,
625 | 'hand-o-up': FaHandOUp,
626 | 'hashtag': FaHashtag,
627 | 'hdd-o': FaHddO,
628 | 'heart': EggheadHeart,
629 | 'help': EggheadHelp,
630 | 'history': EggheadHistory,
631 | 'home': FaHome,
632 | 'hourglass': FaHourglass,
633 | 'hourglass1': FaHourglass1,
634 | 'hourglass2': FaHourglass2,
635 | 'hourglass3': FaHourglass3,
636 | 'hourglassO': FaHourglassO,
637 | 'html5': FaHtml5,
638 | 'iCursor': FaICursor,
639 | 'image': FaImage,
640 | 'inbox': FaInbox,
641 | 'indent': FaIndent,
642 | 'info': EggheadInfo,
643 | 'info-circle': FaInfoCircle,
644 | 'instagram': FaInstagram,
645 | 'italic': FaItalic,
646 | 'jsfiddle': FaJsfiddle,
647 | 'key': FaKey,
648 | 'keyboard-o': FaKeyboardO,
649 | 'laptop': FaLaptop,
650 | 'leaf': FaLeaf,
651 | 'lesson': FaFileO,
652 | 'lesson-headline': EggheadQuickLessonsHeadline,
653 | 'lesson-play-playlist': EggheadLessonPlayPlaylist,
654 | 'level-down': FaLevelDown,
655 | 'level-up': FaLevelUp,
656 | 'lightbulb-o': FaLightbulbO,
657 | 'line-chart': FaLineChart,
658 | 'list': FaList,
659 | 'list-alt': FaListAlt,
660 | 'list-ol': FaListOl,
661 | 'list-ul': FaListUl,
662 | 'locationArrow': FaLocationArrow,
663 | 'lock': FaLock,
664 | 'long-arrow-down': FaLongArrowDown,
665 | 'long-arrow-left': FaLongArrowLeft,
666 | 'long-arrow-right': FaLongArrowRight,
667 | 'long-arrow-up': FaLongArrowUp,
668 | 'magic': FaMagic,
669 | 'magnet': FaMagnet,
670 | 'mail': EggheadMail,
671 | 'mail-forward': FaMailForward,
672 | 'mail-reply': FaMailReply,
673 | 'mail-reply-all': FaMailReplyAll,
674 | 'map': FaMap,
675 | 'map-pin': FaMapPin,
676 | 'map-signs': FaMapSigns,
677 | 'meh-o': FaMehO,
678 | 'menu': FaBars,
679 | 'microphone': FaMicrophone,
680 | 'microphone-slash': FaMicrophoneSlash,
681 | 'minus': FaMinus,
682 | 'minus-circle': FaMinusCircle,
683 | 'minus-square': FaMinusSquare,
684 | 'minus-square-o': FaMinusSquareO,
685 | 'mobile': FaMobile,
686 | 'money': FaMoney,
687 | 'moon-o': FaMoonO,
688 | 'mouse-pointer': FaMousePointer,
689 | 'music': FaMusic,
690 | 'newspaper-o': FaNewspaperO,
691 | 'object-group': FaObjectGroup,
692 | 'object-ungroup': FaObjectUngroup,
693 | 'paper-plane': FaPaperPlane,
694 | 'paper-plane-o': FaPaperPlaneO,
695 | 'paperclip': FaPaperclip,
696 | 'paragraph': FaParagraph,
697 | 'pause': FaPause,
698 | 'pause-circle': FaPauseCircle,
699 | 'pause-circle-o': FaPauseCircleO,
700 | 'pencil': FaPencil,
701 | 'pencil-square': FaPencilSquare,
702 | 'percent': FaPercent,
703 | 'phone': FaPhone,
704 | 'phone-square': FaPhoneSquare,
705 | 'pie-chart': FaPieChart,
706 | 'play': EggheadPlay,
707 | 'playbook': EggheadPlaybook,
708 | 'play-course': EggheadPlayCourse,
709 | 'play-lesson': EggheadPlayLesson,
710 | 'plug': FaPlug,
711 | 'plus-circle': FaPlusCircle,
712 | 'plus-square': FaPlusSquare,
713 | 'plus-square-o': FaPlusSquareO,
714 | 'print': FaPrint,
715 | 'profile': EggheadProfile,
716 | 'progress-ring': EggheadProgressRing,
717 | 'question': EggheadQuestion,
718 | 'question-circle': FaQuestionCircle,
719 | 'question-circle-o': FaQuestionCircleO,
720 | 'quote-left': FaQuoteLeft,
721 | 'quote-right': FaQuoteRight,
722 | 'recycle': FaRecycle,
723 | 'refresh': EggheadRefresh,
724 | 'registered': FaRegistered,
725 | 'remove': FaMinusCircle,
726 | 'repeat': FaRepeat,
727 | 'reply': FaMailReply,
728 | 'request': EggheadRequest,
729 | 'retweet': FaRetweet,
730 | 'revise': EggheadRevise,
731 | 'rotate-left': FaRotateLeft,
732 | 'rss': EggheadRss,
733 | 'rss-square': FaRssSquare,
734 | 'search': FaSearch,
735 | 'search-minus': FaSearchMinus,
736 | 'search-plus': FaSearchPlus,
737 | 'server': FaServer,
738 | 'share': FaShareAlt,
739 | 'share-alt': FaShareAlt,
740 | 'share-alt-square': FaShareAltSquare,
741 | 'share-square': FaShareSquare,
742 | 'share-square-o': FaShareSquareO,
743 | 'shopping-cart': FaShoppingCart,
744 | 'sign-out': EggheadSignOut,
745 | 'sign-in': FaSignIn,
746 | 'signal': FaSignal,
747 | 'sitemap': FaSitemap,
748 | 'slack': FaSlack,
749 | 'sliders': FaSliders,
750 | 'smile-o': FaSmileO,
751 | 'sort': FaSort,
752 | 'sort-alpha-asc': FaSortAlphaAsc,
753 | 'sort-alpha-desc': FaSortAlphaDesc,
754 | 'sort-amount-asc': FaSortAmountAsc,
755 | 'sort-amount-desc': FaSortAmountDesc,
756 | 'sort-asc': FaSortAsc,
757 | 'sort-desc': FaSortDesc,
758 | 'sort-numeric-asc': FaSortNumericAsc,
759 | 'sort-numeric-desc': FaSortNumericDesc,
760 | 'spinner': FaSpinner,
761 | 'square': FaSquare,
762 | 'star': FaStar,
763 | 'star-fill': EggheadStarFill,
764 | 'star-outline': EggheadStarOutline,
765 | 'star-half': FaStarHalf,
766 | 'star-half-empty': FaStarHalfEmpty,
767 | 'star-o': FaStarO,
768 | 'stats': EggheadStats,
769 | 'step-backward': FaStepBackward,
770 | 'step-forward': FaStepForward,
771 | 'sticky-note': FaStickyNote,
772 | 'sticky-note-o': FaStickyNoteO,
773 | 'stop': FaStop,
774 | 'stop-circle': FaStopCircle,
775 | 'stop-circle-o': FaStopCircleO,
776 | 'success': FaCheckCircle,
777 | 'sun-o': FaSunO,
778 | 'table': FaTable,
779 | 'tablet': FaTablet,
780 | 'tag': FaTag,
781 | 'tags': FaTags,
782 | 'tasks': FaTasks,
783 | 'technology': EggheadTechnology,
784 | 'television': FaTelevision,
785 | 'terminal': FaTerminal,
786 | 'text-height': FaTextHeight,
787 | 'text-width': FaTextWidth,
788 | 'th': FaTh,
789 | 'th-large': FaThLarge,
790 | 'th-list': FaThList,
791 | 'thumb-tack': FaThumbTack,
792 | 'thumbs-down': EggheadThumbsDown,
793 | 'thumbs-up': EggheadThumbsUp,
794 | 'thumbs-o-down': FaThumbsODown,
795 | 'thumbs-o-up': FaThumbsOUp,
796 | 'ticket': FaTicket,
797 | 'timer-plus': EggheadTimerPlus,
798 | 'times-circle': FaTimesCircle,
799 | 'times-circle-o': FaTimesCircleO,
800 | 'toggle-off': FaToggleOff,
801 | 'toggle-on': FaToggleOn,
802 | 'trademark': FaTrademark,
803 | 'trash': FaTrash,
804 | 'trash-o': FaTrashO,
805 | 'tree': FaTree,
806 | 'trophy': FaTrophy,
807 | 'twitter': EggheadTwitter,
808 | 'twitter-square': FaTwitterSquare,
809 | 'underline': FaUnderline,
810 | 'universal-access': FaUniversalAccess,
811 | 'unlock': FaUnlock,
812 | 'unlock-alt': FaUnlockAlt,
813 | 'update': EggheadUpdate,
814 | 'upload': EggheadUpload,
815 | 'upload-cloud': EggheadUploadCloud,
816 | 'usb': FaUsb,
817 | 'user': FaUser,
818 | 'user-plus': FaUserPlus,
819 | 'user-times': FaUserTimes,
820 | 'video-camera': FaVideoCamera,
821 | 'vimeo-square': FaVimeoSquare,
822 | 'volume-control-phone': FaVolumeControlPhone,
823 | 'volume-down': FaVolumeDown,
824 | 'volume-off': FaVolumeOff,
825 | 'volume-up': FaVolumeUp,
826 | 'warning': FaExclamationCircle,
827 | 'wheelchair': FaWheelchair,
828 | 'wheelchairAlt': FaWheelchairAlt,
829 | 'wifi': FaWifi,
830 | 'windows': FaWindows,
831 | 'wrench': FaWrench,
832 | }
833 |
834 | export const types = keys(typeToSvgIcon)
835 |
836 | const sizeToClass = {
837 | 1: 'f1',
838 | 2: 'f2',
839 | 3: 'f3',
840 | 4: 'f4',
841 | 5: 'f5',
842 | 6: 'f6'
843 | }
844 |
845 | export const sizes = keys(sizeToClass)
846 |
847 | const Icon = ({
848 | type,
849 | size,
850 | color,
851 | }) => {
852 | const DynamicIcon = typeToSvgIcon[type]
853 | return
854 | }
855 |
856 | Icon.propTypes = {
857 | type: PropTypes.oneOf(types).isRequired,
858 | size: PropTypes.oneOf(sizes),
859 | color: PropTypes.oneOf(colors),
860 | }
861 |
862 | Icon.defaultProps = {
863 | size: '3',
864 | color: 'dark-gray',
865 | }
866 |
867 | export default Icon
868 |
--------------------------------------------------------------------------------
/src/App/utils/resourcesByType/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {internet, lorem, random, name} from 'faker'
3 |
4 | import Anchor, {types as anchorTypes} from 'package/components/Anchor'
5 | import Avatar, {sizes as avatarSizes} from 'package/components/Avatar'
6 | import Button, {sizes as buttonSizes} from 'package/components/Button'
7 | import Card, {levels as cardLevels} from 'package/components/Card'
8 | import ContainerWidth from 'package/components/ContainerWidth'
9 | import Error from 'package/components/Error'
10 | import HeaderCard from 'package/components/HeaderCard'
11 | import Heading, {levels as headingLevels} from 'package/components/Heading'
12 | import Icon, {types as iconTypes, sizes as iconSizes} from 'package/components/Icon'
13 | import IconLabel from 'package/components/IconLabel'
14 | import Image from 'package/components/Image'
15 | import InstructorLessons from 'package/components/InstructorLessons'
16 | import InstructorRevenue from 'package/components/InstructorRevenue'
17 | import InstructorStats from 'package/components/InstructorStats'
18 | import LayoutColumns from 'package/components/LayoutColumns'
19 | import LessonActions from 'package/components/LessonActions'
20 | import LessonOverviews from 'package/components/LessonOverviews'
21 | import LessonOverviewsByGroup from 'package/components/LessonOverviewsByGroup'
22 | import List, {sizes as listSizes} from 'package/components/List'
23 | import Loading from 'package/components/Loading'
24 | import Markdown from 'package/components/Markdown'
25 | import Maybe from 'package/components/Maybe'
26 | import Open from 'package/components/Open'
27 | import Paragraph, {types as paragraphTypes} from 'package/components/Paragraph'
28 | import Prompt from 'package/components/Prompt'
29 | import Request, {methods as requestMethods} from 'package/components/Request'
30 | import RequestedLessons from 'package/components/RequestedLessons'
31 | import Tabs from 'package/components/Tabs'
32 | import Toggle, {selectedItems as toggleSelectedItems} from 'package/components/Toggle'
33 | import ViewportWidth from 'package/components/ViewportWidth'
34 |
35 | import InstructorDashboard from 'package/screens/InstructorDashboard'
36 | import InstructorDetails from 'package/screens/InstructorDetails'
37 | import InstructorsDirectory from 'package/screens/InstructorsDirectory'
38 | import LessonDetails from 'package/screens/LessonDetails'
39 | import LessonsDirectory from 'package/screens/LessonsDirectory'
40 | import NewLesson from 'package/screens/NewLesson'
41 |
42 | import colors from 'package/utils/colors'
43 | import colorValues from 'package/utils/colorValues'
44 | import login from 'package/utils/login'
45 | import logout from 'package/utils/logout'
46 | import {containerWidths} from 'package/utils/hardCodedSizes'
47 | import {lessonStates} from 'package/utils/lessonStates'
48 | import windowMock from 'package/utils/windowMock'
49 |
50 | import Authentication from 'App/components/Authentication'
51 |
52 | const universalWindow = typeof(window) === 'undefined'
53 | ? windowMock
54 | : window
55 |
56 | const getLoginUrl = () =>
57 | `${process.env.REACT_APP_EGGHEAD_BASE_URL}/users/jwt?return_to=${universalWindow.location.href}`
58 |
59 | const createNodeExample = () => random.arrayElement([
60 |
61 |
62 |
68 |
69 | {lorem.words()}
70 |
71 |
,
72 |
73 |
74 |
78 |
82 |
,
83 |
84 |
85 |
86 | {lorem.paragraph()}
87 |
88 |
89 | {lorem.words()}
90 |
91 |
,
92 |
93 | ])
94 |
95 | export const resourcesByType = {
96 |
97 | components: {
98 | urlBase: '/components',
99 | items: {
100 |
101 | Anchor: {
102 | useCase: `Used in place of the inline html to link to other pages.`,
103 | types: {
104 | 'children*': 'node',
105 | 'url*': 'string',
106 | 'isSeparateTab': 'bool',
107 | 'type': anchorTypes,
108 | 'color': colors,
109 | },
110 | createExamples: () => [
111 |
112 | {lorem.words()}
113 | ,
114 |
118 | {lorem.words()}
119 | ,
120 |
124 | {lorem.words()}
125 | ,
126 | ],
127 | },
128 |
129 | Avatar: {
130 | useCase: `Used to display a person's picture.`,
131 | types: {
132 | 'name*': 'string',
133 | 'url*': 'string',
134 | 'size': avatarSizes,
135 | },
136 | createExamples: () => [
137 | ,
141 | ,
146 | ],
147 | },
148 |
149 | Button: {
150 | useCase: `Used to give the user an action to take.`,
151 | types: {
152 | 'children*': 'node',
153 | 'onClick': 'func',
154 | 'size': buttonSizes,
155 | 'color': colors,
156 | 'outline': 'bool',
157 | 'overDark': 'bool',
158 | },
159 | createExamples: () => [
160 |
161 | {lorem.words()}
162 | ,
163 |
169 | {lorem.words()}
170 | ,
171 | ],
172 | },
173 |
174 | Card: {
175 | useCase: `Used to display related node(s) in a container with a hierarchy.`,
176 | types: {
177 | 'children*': 'node',
178 | 'level': cardLevels,
179 | },
180 | createExamples: () => [
181 |
182 | {lorem.words()}
183 | ,
184 |
185 | {createNodeExample()}
186 | ,
187 | ],
188 | },
189 |
190 | ContainerWidth: {
191 | useCase: `Used to get the current node's container width.`,
192 | types: {
193 | 'children*': 'func',
194 | 'onWidthChange': 'func',
195 | },
196 | childrenTypes: {
197 | 'containerWidth': containerWidths,
198 | },
199 | createExamples: () => [
200 |
201 | {(containerWidth) => containerWidth: {containerWidth}
}
202 | ,
203 | ],
204 | optOut: ['types', 'containerBackground'],
205 | },
206 |
207 | Error: {
208 | useCase: `Used to display an error to the user.`,
209 | types: {
210 | 'children*': 'string',
211 | },
212 | createExamples: () => [
213 |
214 | {lorem.sentence()}
215 | ,
216 | ],
217 | },
218 |
219 | HeaderCard: {
220 | useCase: `Used to display a Card with a header.`,
221 | types: {
222 | 'children*': 'node',
223 | 'title*': 'string',
224 | 'description': 'string',
225 | 'intro': 'node',
226 | 'subtle': 'bool',
227 | },
228 | createExamples: () => [
229 |
230 | {createNodeExample()}
231 | ,
232 |
238 | {createNodeExample()}
239 | ,
240 | ],
241 | },
242 |
243 | Heading: {
244 | useCase: `Used to label related node(s) with a hierarchy.`,
245 | types: {
246 | 'children*': 'string',
247 | 'level': headingLevels,
248 | },
249 | createExamples: () => [
250 |
251 | {lorem.words()}
252 | ,
253 | ],
254 | },
255 |
256 | Icon: {
257 | useCase: `Used to display a vector graphic.`,
258 | types: {
259 | 'type*': iconTypes,
260 | 'size': iconSizes,
261 | 'color': colors,
262 | },
263 | createExamples: () => [
264 | ,
265 | ,
270 | ],
271 | },
272 |
273 | IconLabel: {
274 | useCase: `Used to display a vector graphic with a label.`,
275 | types: {
276 | 'iconType*': iconTypes,
277 | 'labelText*': 'string',
278 | 'color': colors,
279 | },
280 | createExamples: () => [
281 | ,
285 | ,
290 | ],
291 | },
292 |
293 | Image: {
294 | useCase: `Used to display a raster graphic.`,
295 | types: {
296 | 'src*': 'string',
297 | 'alt*': 'string',
298 | 'className': 'string',
299 | },
300 | createExamples: () => [
301 | ,
305 | ],
306 | },
307 |
308 | InstructorLessons: {
309 | useCase: `Used to display the lessons of a specific instructor.`,
310 | types: {
311 | 'instructor*': 'object',
312 | },
313 | createExamples: () => [
314 |
315 | {({data}) => (
316 |
317 | {({data}) => (
318 |
319 | )}
320 |
321 | )}
322 | ,
323 | ],
324 | },
325 |
326 | InstructorRevenue: {
327 | useCase: `Used to display the revenue of a specific instructor.`,
328 | types: {
329 | 'revenueUrl*': 'string',
330 | },
331 | createExamples: () => [
332 |
333 | {({data}) => (
334 |
335 | {({data}) => (
336 |
337 | )}
338 |
339 | )}
340 | ,
341 | ],
342 | },
343 |
344 | InstructorStats: {
345 | useCase: `Used to display the stats of a specific instructor.`,
346 | types: {
347 | 'instructor*': 'object',
348 | },
349 | createExamples: () => [
350 |
351 | {({data}) => (
352 |
353 | {({data}) => (
354 |
355 | )}
356 |
357 | )}
358 | ,
359 | ],
360 | },
361 |
362 | LayoutColumns: {
363 | useCase: `Used to group nodes into columns when the container's width is large enough; otherwise the groups stack vertically.`,
364 | types: {
365 | 'items*': '[node]',
366 | 'relativeSizes': '[number]',
367 | },
368 | createExamples: () => [
369 | ,
376 | ,
389 | ],
390 | },
391 |
392 | LessonActions: {
393 | useCase: `Used to take action on a specific lesson.`,
394 | types: {
395 | 'lesson*': 'object',
396 | 'requestLesson': 'func',
397 | 'requestCurrentPage': 'func',
398 | },
399 | createExamples: () => [
400 |
401 | {({data}) => (
402 |
403 | {({request, data}) => (
404 |
408 | )}
409 |
410 | )}
411 | ,
412 | ],
413 | },
414 |
415 | LessonOverviews: {
416 | useCase: `Used to show a collection of lessons.`,
417 | types: {
418 | 'states*': lessonStates,
419 | 'fallback*': 'node',
420 | 'instructor': 'object',
421 | 'pageSize': 'number',
422 | 'includeLessonsInCourses': 'bool',
423 | },
424 | createExamples: () => [
425 |
426 | {({data}) => {
427 | const rootData = data
428 | return (
429 |
430 | {({data}) => (
431 |
439 | }
440 | lessonsUrl={rootData.lessons_url}
441 | includeLessonsInCourses
442 | />
443 | )}
444 |
445 | )
446 | }}
447 | ,
448 | ],
449 | },
450 |
451 | LessonOverviewsByGroup: {
452 | useCase: `Used to show a collection of lessons organized by lesson state groups.`,
453 | types: {
454 | 'instructor': 'object',
455 | },
456 | createExamples: () => [
457 |
458 | {({data}) => (
459 |
460 | {({data}) => {
461 | const instructor = random.arrayElement(data)
462 | return (
463 |
467 | )
468 | }}
469 |
470 | )}
471 | ,
472 | ],
473 | },
474 |
475 | List: {
476 | useCase: `Used to show a collection of nodes in a vertical stack.`,
477 | types: {
478 | 'items*': '[node]',
479 | 'size': listSizes,
480 | 'overDark': 'bool',
481 | },
482 | createExamples: () => [
483 |
,
490 |
,
499 | ]
500 | },
501 |
502 | Loading: {
503 | useCase: `Used to let the user know something is loading.`,
504 | createExamples: () => [
505 | ,
506 | ],
507 | optOut: ['types'],
508 | },
509 |
510 | Markdown: {
511 | useCase: `Used to display markdown strings.`,
512 | types: {
513 | 'children*': 'string',
514 | },
515 | createExamples: () => [
516 |
517 | {`${lorem.words()} \`${lorem.word()}\` **${lorem.words()}**`}
518 | ,
519 | ],
520 | },
521 |
522 | Maybe: {
523 | useCase: `Used to conditionally display a node or null.`,
524 | types: {
525 | 'children*': 'node',
526 | 'condition*': 'bool',
527 | },
528 | createExamples: () => [
529 |
530 | {createNodeExample()}
531 | ,
532 | ],
533 | optOut: ['containerWidth', 'containerBackground', 'containerPadding'],
534 | },
535 |
536 | Open: {
537 | useCase: `Used to toggle a boolean state.`,
538 | types: {
539 | 'children*': 'func',
540 | },
541 | childrenTypes: {
542 | 'isOpen': 'bool',
543 | 'handleOpenToggleClick': 'func',
544 | },
545 | createExamples: () => [
546 |
547 | {({isOpen, handleOpenToggleClick}) => (
548 |
549 |
550 | isOpen: {`${isOpen}`}
551 |
552 |
557 | Example click handler
558 |
559 |
560 | )}
561 |
562 | ],
563 | optOut: ['containerWidth', 'containerBackground', 'containerPadding', 'types'],
564 | },
565 |
566 | Paragraph: {
567 | useCase: `Used to display a paragraph of text.`,
568 | types: {
569 | 'children*': 'string',
570 | 'type': paragraphTypes,
571 | },
572 | createExamples: () => [
573 |
574 | {lorem.paragraph()}
575 | ,
576 |
577 | {lorem.paragraph()}
578 |
579 | ],
580 | },
581 |
582 | Prompt: {
583 | useCase: `Used as a fallback to prompt the user to take action.`,
584 | types: {
585 | 'description*': 'string',
586 | 'actionText*': 'string',
587 | 'action*': 'string',
588 | },
589 | createExamples: () => [
590 | ,
595 | ],
596 | },
597 |
598 | Request: {
599 | useCase: `Used to make requests to endpoints.`,
600 | types: {
601 | 'children*': 'func',
602 | 'url*': 'string',
603 | 'lazy': 'bool',
604 | 'placeholder': 'node',
605 | 'params': 'object',
606 | 'headers': 'object',
607 | 'body': 'object',
608 | 'onResponse': 'func',
609 | 'onData': 'func',
610 | 'onError': 'func',
611 | 'method': requestMethods,
612 | 'subscribe': 'bool',
613 | 'subscribeInterval': 'number',
614 | 'showLoadingBetweenRequests': 'bool',
615 | },
616 | childrenTypes: {
617 | 'handleOpenToggleClick': 'func',
618 | 'request': 'func',
619 | 'running': 'bool',
620 | 'error': 'object',
621 | 'data': 'object',
622 | 'response': 'object',
623 | },
624 | createExamples: () => [
625 |
626 | {({data}) => (
627 |
628 | {JSON.stringify(data, null, 2)}
629 |
630 | )}
631 | ,
632 |
633 | {({data}) => (
634 |
635 | {JSON.stringify(data, null, 2)}
636 |
637 | )}
638 | ,
639 |
643 | {({request, data}) => data
644 | ?
645 | {JSON.stringify(data, null, 2)}
646 |
647 | : request()}
649 | className='pointer dim'
650 | >
651 | Example click handler
652 |
653 | }
654 |
655 | ],
656 | optOut: ['containerWidth', 'containerBackground', 'containerPadding', 'types'],
657 | },
658 |
659 | RequestedLessons: {
660 | useCase: `Used to give instructors a list of options for starting a new lesson.`,
661 | types: {
662 | 'instructor*': 'object',
663 | },
664 | createExamples: () => [
665 |
666 | {({data}) => {
667 | const rootData = data
668 | return (
669 |
670 | {({data}) => (
671 |
676 | )}
677 |
678 | )
679 | }}
680 | ,
681 | ],
682 | optOut: ['types'],
683 | },
684 |
685 | Tabs: {
686 | useCase: `Used to group related nodes into tabs.`,
687 | types: {
688 | 'groups*': '[{title: string, component: node}]',
689 | },
690 | createExamples: () => [
691 | ,
705 | ],
706 | },
707 |
708 | Toggle: {
709 | useCase: `Used to let the user toggle a boolean state choice.`,
710 | types: {
711 | 'leftOption*': 'string',
712 | 'rightOption*': 'string',
713 | 'onClick': 'func',
714 | 'selectedItem': toggleSelectedItems,
715 | },
716 | createExamples: () => [
717 | ,
721 | ,
726 | ],
727 | },
728 |
729 | ViewportWidth: {
730 | useCase: `Used to get the current browser width. ContainerWidth is preferred in most situations unless the entire viewport width is needed.`,
731 | types: {
732 | 'children*': 'func',
733 | 'onWidthChange': 'func',
734 | },
735 | childrenTypes: {
736 | 'isLikelyDesktop': 'bool',
737 | },
738 | createExamples: () => [
739 |
740 | {(isLikelyDesktop) => isLikelyDesktop: {`${isLikelyDesktop}`}
}
741 | ,
742 | ],
743 | optOut: ['containerBackground', 'containerPadding', 'types'],
744 | },
745 |
746 | }
747 | },
748 |
749 | screens: {
750 | urlBase: '/screens',
751 | items: {
752 |
753 | InstructorDashboard: {
754 | useCase: `Used to show instructors their most important information. If the instructor is unpublished, it shows information for getting published. Once the instructor is published it shows the "normal" Instructor Dashboard.`,
755 | types: {
756 | 'instructor*': 'object',
757 | },
758 | createExamples: () => [
759 |
760 | {({data}) => {
761 | const rootData = data
762 | return (
763 |
764 | {({data}) => (
765 |
770 | )}
771 |
772 | )
773 | }}
774 | ,
775 | ],
776 | },
777 |
778 | InstructorDetails: {
779 | useCase: `Used to show full details about a specific instructor.`,
780 | types: {
781 | 'instructor*': 'object',
782 | },
783 | createExamples: () => [
784 |
785 | {({data}) => (
786 |
787 | {({data}) => (
788 |
789 | )}
790 |
791 | )}
792 | ,
793 | ],
794 | },
795 |
796 | InstructorsDirectory: {
797 | useCase: `Used to show a list of all instructors.`,
798 | types: {
799 | 'instructor*': 'object',
800 | },
801 | createExamples: () => [
802 |
803 | {({data}) => (
804 |
805 | )}
806 | ,
807 | ],
808 | },
809 |
810 | LessonDetails: {
811 | useCase: `Used to show full details about a specific lesson.`,
812 | types: {
813 | 'lesson*': 'object',
814 | 'requestLesson': 'func',
815 | },
816 | createExamples: () => [
817 |
818 | {({data}) => (
819 |
820 | {({request, data}) => (
821 |
825 | )}
826 |
827 | )}
828 | ,
829 | ],
830 | },
831 |
832 | LessonsDirectory: {
833 | useCase: `Used to show a list of all lessons.`,
834 | createExamples: () => [
835 |
836 | {({data}) => (
837 |
838 | )}
839 | ,
840 | ],
841 | },
842 |
843 | NewLesson: {
844 | useCase: `Used to help instructors start a new lesson.`,
845 | types: {
846 | 'instructor*': 'object',
847 | },
848 | createExamples: () => [
849 |
850 | {({data}) => {
851 | const rootData = data
852 | return (
853 |
854 | {({data}) => (
855 |
860 | )}
861 |
862 | )
863 | }}
864 | ,
865 | ],
866 | },
867 |
868 | },
869 | },
870 |
871 | utilities: {
872 | urlBase: '/utils',
873 | items: {
874 |
875 | colors: {
876 | useCase: `An array of the egghead color keys.`,
877 | createExamples: () => [
878 | colors,
879 | ],
880 | optOut: ['containerWidth', 'containerBackground', 'containerPadding', 'types'],
881 | },
882 |
883 | colorValues: {
884 | useCase: `An array of the egghead color keys and values.`,
885 | types: {
886 | 'key*': colors,
887 | },
888 | createExamples: () => [
889 | colorValues[random.arrayElement(colors)],
890 | ],
891 | optOut: ['containerWidth', 'containerBackground', 'containerPadding'],
892 | },
893 |
894 | login: {
895 | useCase: `Uses local storage "token" if it exists. Adds/updates local storage "token" if there is a "jwt" query param in the URL from an egghead login URL redirect.`,
896 | createExamples: () => [
897 | login(),
898 | ],
899 | },
900 |
901 | logout: {
902 | useCase: `Removes "token" from local storage and refreshes the page to log the user out.`,
903 | createExamples: () => [
904 | logout(),
905 | ],
906 | },
907 |
908 | },
909 | },
910 | }
911 |
912 | export default resourcesByType
913 |
--------------------------------------------------------------------------------