├── 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 | 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 |
13 | {children} 14 |
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 | {graphic.name} 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 | {alt} 24 | ) 25 | } 26 | 27 | return ( 28 |
29 | {alt} 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 |
24 |
30 |
37 |
38 |
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 | ?
14 |
15 |