├── .eslintignore ├── .eslintrc.json ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── add-depr-ticket-to-depr-board.yml │ ├── add-remove-label-on-comment.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── npm-publish.yml │ └── self-assign-issue.yml ├── .gitignore ├── .npmignore ├── .nvmrc ├── AUTHORS ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── _config.yml ├── bower.json ├── catalog-info.yaml ├── doc ├── 404.html ├── CNAME ├── _data │ ├── edx-pattern-library.yml │ └── edx-ui-toolkit.yml ├── _includes │ ├── assets-primary.html │ ├── assets-secondary.html │ ├── collection-page-list.html │ ├── footer.html │ ├── head.html │ ├── header.html │ ├── navigation-item.html │ └── navigation.html ├── _layouts │ └── default.html ├── favicon.ico ├── index.md └── static │ ├── images │ └── edx-logo.svg │ ├── js │ └── ui-toolkit-doc-factory.js │ └── sass │ ├── _build.scss │ ├── _components.scss │ ├── _layouts.scss │ ├── _syntax.scss │ ├── _utilities.scss │ ├── main-ltr.scss │ └── main-rtl.scss ├── gulp ├── .eslintrc.json ├── config.js ├── tasks │ ├── clean.js │ ├── default.js │ ├── doc.js │ ├── lint.js │ ├── preview.js │ ├── test-debug.js │ └── test.js └── utils │ ├── generate-doc.js │ └── git-utils.js ├── gulpfile.js ├── openedx.yaml ├── package-lock.json ├── package.json ├── renovate.json ├── src └── js │ ├── breadcrumbs │ ├── breadcrumbs-model.js │ ├── breadcrumbs-view.js │ ├── breadcrumbs.underscore │ └── specs │ │ └── breadcrumbs-spec.js │ ├── disclosure │ ├── disclosure-view.js │ └── specs │ │ └── disclosure-view-spec.js │ ├── dropdown-menu │ ├── dropdown-menu-view.js │ ├── dropdown.underscore │ └── specs │ │ └── dropdown-menu-view-spec.js │ ├── pagination │ ├── paging-collection.js │ └── specs │ │ └── paging-collection-spec.js │ └── utils │ ├── constants.js │ ├── date-utils.js │ ├── global-loader.js │ ├── html-utils.js │ ├── spec-helpers │ ├── ajax-helpers.js │ └── spec-helpers.js │ ├── specs │ ├── ajax-helpers-spec.js │ ├── date-utils-spec.js │ ├── global-loader-spec.js │ ├── html-utils-spec.js │ └── string-utils-spec.js │ └── string-utils.js ├── test ├── jquery.simulate.js ├── karma.ci.conf.js ├── karma.conf.js ├── karma.debug.conf.js ├── require-config.js └── spec-runner.js └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | test/jquery.simulate.js 2 | node_modules 3 | build 4 | coverage 5 | _site 6 | _preview_site 7 | doc/public 8 | doc/_testing 9 | doc/_utilities 10 | doc/_views 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "amd": true 4 | }, 5 | "extends": "@edx/eslint-config", 6 | "globals": { 7 | "setFixtures": true // added globally by jasmine-jquery (bad) 8 | }, 9 | "rules": { 10 | "indent": "off", 11 | "func-names": "off", 12 | "import/no-extraneous-dependencies": "off", 13 | "object-shorthand": "off", 14 | "no-undef": "off", 15 | "no-var": "off", 16 | "one-var": "off", 17 | "prefer-arrow-callback": "off", 18 | "function-paren-newline": "off", 19 | "one-var-declaration-per-line": "off", 20 | "import/no-amd": "off", 21 | "no-param-reassign": "off", 22 | "new-cap": "off", 23 | "comma-dangle": "off", 24 | "object-curly-spacing": "off", 25 | "space-before-function-paren": "off", 26 | "prefer-template": "off", 27 | "operator-linebreak": "off", 28 | "no-multi-spaces": "off", 29 | "prefer-spread": "off", 30 | "prefer-rest-params": "off", 31 | "no-else-return": "off", 32 | "no-multiple-empty-lines": "off", 33 | "prefer-destructuring": "off", 34 | "no-buffer-constructor": "off", 35 | "import/order": "off", 36 | "import/extensions": "off", 37 | "import/no-dynamic-require": "off" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | TODO: Link to a JIRA ticket if one has been created for this feature/issue. 4 | Use the following format for a link: `[FEDX-100](https://openedx.atlassian.net/browse/FEDX-100)` 5 | 6 | TODO: Include a detailed description of the changes in the body of the PR. 7 | 8 | TODO: If relevant, provide a URL to updated Pattern Library documentation. 9 | 10 | ## Manual Test Plan 11 | 12 | TODO: If your changes require manual tests, include instructions for any required manual tests, and any manual testing that has already been performed. 13 | 14 | ## Testing Checklist 15 | - [ ] Write unit tests for all new features. 16 | - [ ] Write accessibility (a11y) tests for all new UI. 17 | - [ ] Manually test responsive behavior. 18 | 19 | ## Non-testing Checklist 20 | - [ ] Consider any documentation your change might need, and which users will be affected by this change. 21 | - [ ] Consult with the Pattern Library team if adding a new component. 22 | 23 | ## Post-review 24 | - [ ] Squash commits into discrete sets of changes with descriptive commit messages. 25 | 26 | ## Reviewers 27 | TODO: In this section, tag specific reviewers you know will want to have a look at this PR. You can use the checklist below for organization: 28 | 29 | - [ ] @mention a reviewer here 30 | - [ ] @mention a reviewer here 31 | 32 | TODO: Consider not just code reviewers but also including reviewers from Docs, UX, Accessibility and Product, if applicable. 33 | 34 | If you've been tagged for review, please check your corresponding box once you've given the :+1:. 35 | 36 | We'll also automatically suggest reviewers for this PR based on the code it touches. 37 | -------------------------------------------------------------------------------- /.github/workflows/add-depr-ticket-to-depr-board.yml: -------------------------------------------------------------------------------- 1 | # Run the workflow that adds new tickets that are either: 2 | # - labelled "DEPR" 3 | # - title starts with "[DEPR]" 4 | # - body starts with "Proposal Date" (this is the first template field) 5 | # to the org-wide DEPR project board 6 | 7 | name: Add newly created DEPR issues to the DEPR project board 8 | 9 | on: 10 | issues: 11 | types: [opened] 12 | 13 | jobs: 14 | routeissue: 15 | uses: openedx/.github/.github/workflows/add-depr-ticket-to-depr-board.yml@master 16 | secrets: 17 | GITHUB_APP_ID: ${{ secrets.GRAPHQL_AUTH_APP_ID }} 18 | GITHUB_APP_PRIVATE_KEY: ${{ secrets.GRAPHQL_AUTH_APP_PEM }} 19 | SLACK_BOT_TOKEN: ${{ secrets.SLACK_ISSUE_BOT_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/add-remove-label-on-comment.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs when a comment is made on the ticket 2 | # If the comment starts with "label: " it tries to apply 3 | # the label indicated in rest of comment. 4 | # If the comment starts with "remove label: ", it tries 5 | # to remove the indicated label. 6 | # Note: Labels are allowed to have spaces and this script does 7 | # not parse spaces (as often a space is legitimate), so the command 8 | # "label: really long lots of words label" will apply the 9 | # label "really long lots of words label" 10 | 11 | name: Allows for the adding and removing of labels via comment 12 | 13 | on: 14 | issue_comment: 15 | types: [created] 16 | 17 | jobs: 18 | add_remove_labels: 19 | uses: openedx/.github/.github/workflows/add-remove-label-on-comment.yml@master 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - "**" 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | 18 | - name: Setup Nodejs 19 | uses: actions/setup-node@v4 20 | with: 21 | node-version: 20 22 | 23 | - name: Export CHROME_BIN Env Variable 24 | run: export CHROME_BIN=chromium-browser 25 | 26 | - name: Export Display Variable 27 | run: export DISPLAY=:99.0 28 | 29 | - name: Install Ruby 30 | uses: ruby/setup-ruby@v1 31 | with: 32 | ruby-version: 2.7.3 33 | 34 | - name: Install Dependencies 35 | run: npm install 36 | 37 | - name: Bundle Install 38 | run: bundle install 39 | 40 | - name: Install Firefox 41 | uses: browser-actions/setup-firefox@v1 42 | 43 | - name: Run Gulp 44 | uses: GabrielBB/xvfb-action@v1 45 | with: 46 | run: gulp default --ci 47 | 48 | - name: Run Code Coverage 49 | uses: codecov/codecov-action@v4 50 | with: 51 | token: ${{ secrets.CODECOV_TOKEN }} 52 | fail_ci_if_error: true 53 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | # Run commitlint on the commit messages in a pull request. 2 | 3 | name: Lint Commit Messages 4 | 5 | on: 6 | - pull_request 7 | 8 | jobs: 9 | commitlint: 10 | uses: openedx/.github/.github/workflows/commitlint.yml@master 11 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: Release CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version-file: '.nvmrc' 20 | - name: Install dependencies 21 | run: npm ci 22 | - name: Lint 23 | run: npm run lint 24 | - name: Test 25 | run: npm run test 26 | - name: Release 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.OPENEDX_SEMANTIC_RELEASE_GITHUB_TOKEN }} 29 | NPM_TOKEN: ${{ secrets.OPENEDX_SEMANTIC_RELEASE_NPM_TOKEN }} 30 | run: npx semantic-release@19.0.5 31 | -------------------------------------------------------------------------------- /.github/workflows/self-assign-issue.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs when a comment is made on the ticket 2 | # If the comment starts with "assign me" it assigns the author to the 3 | # ticket (case insensitive) 4 | 5 | name: Assign comment author to ticket if they say "assign me" 6 | on: 7 | issue_comment: 8 | types: [created] 9 | 10 | jobs: 11 | self_assign_by_comment: 12 | uses: openedx/.github/.github/workflows/self-assign-issue.yml@master 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python bytecode: 2 | *.py[co] 3 | 4 | # Packaging files: 5 | *.egg* 6 | 7 | # Sphinx docs: 8 | build 9 | 10 | # SQLite3 database files: 11 | *.db 12 | 13 | # Logs: 14 | *.log 15 | 16 | # SQLite3 database files: 17 | *.db 18 | 19 | # Logs: 20 | *.log 21 | 22 | # IDE 23 | .idea/ 24 | .project 25 | .pydevproject 26 | .settings 27 | 28 | # Linux Editors 29 | *~ 30 | \#*\# 31 | /.emacs.desktop 32 | /.emacs.desktop.lock 33 | .elc 34 | auto-save-list 35 | tramp 36 | .\#* 37 | *.swp 38 | *.swo 39 | 40 | # Linters 41 | .eslintcache 42 | 43 | # Mac 44 | .DS_Store 45 | ._* 46 | 47 | # Windows 48 | Thumbs.db 49 | Desktop.ini 50 | 51 | # UI Toolkit 52 | *.map 53 | .publish 54 | .sass-cache 55 | doc/public 56 | doc/_testing 57 | doc/_utilities 58 | doc/_views 59 | node_modules 60 | _preview_site 61 | _site 62 | _tmp_preview_config.yml 63 | 64 | coverage/coverage-js/ 65 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | .gitignore 3 | .npmignore 4 | _config.yml 5 | _preview_site 6 | _site 7 | bower.json 8 | build 9 | coverage 10 | doc 11 | Gemfile 12 | Gemfile.lock 13 | gulp 14 | gulpfile.js 15 | index.html 16 | node_modules 17 | package.json 18 | specs 19 | test 20 | webpack.config.js 21 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Dennis Jen 2 | Alasdair Swan 3 | Andy Armstrong 4 | Alisan Tang 5 | Gregory Martin 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.5.3 (2017-04-21) 4 | * Upgrade eslint and add new eslint configs (eslint-config-edx, eslint-config-edx-es5) 5 | * Fix resulting linter errors 6 | 7 | - - - 8 | 9 | ## 1.5.2 (2017-04-10) 10 | * Update and pin dependencies 11 | * Introduce yarn for dependency management 12 | * Add alternative to deprecated keyCodes 13 | 14 | - - - 15 | 16 | ## 1.5.1 (2017-01-03) 17 | * Add two format constants to DateUtils, update format comments 18 | 19 | - - - 20 | 21 | ## 1.5.0 (2016-10-03) 22 | * Add DateUtils 23 | 24 | - - - 25 | 26 | ## 1.4.1 (2016-06-24) 27 | * Fix minor issue with unnecessary DOM node in breadcrumbs 28 | 29 | - - - 30 | 31 | ## 1.4.0 (2016-06-21) 32 | * Add model and view for accessible breadcrumb navigation 33 | 34 | - - - 35 | 36 | ## 1.3.0 (2016-06-21) 37 | * Extend AjaxHelpers with capabilities from edx-platform 38 | 39 | - - - 40 | 41 | ## 1.2.0 (2016-06-14) 42 | * In dropdown menu component icons switched to Font Awesome for accessibility 43 | 44 | - - - 45 | 46 | ## 1.1.0 (2016-05-26) 47 | * Removed parseState method from PagingCollection in favor of PageableCollection 48 | * Added getters in PagingCollection to get the state attributes 49 | 50 | - - - 51 | 52 | ## 1.0.0 (2016-05-17) 53 | * First public release of the UI Toolkit! 54 | * API documentation available at http://ui-toolkit.edx.org 55 | * Minor bug fixes 56 | 57 | - - - 58 | 59 | ## 0.10.0 (2016-04-14) 60 | * Update README to gulp test-debug (with a hyphen) 61 | * Update getActivefilterfields, add getFilterFieldValue 62 | * Fix issues with karma hanging on exit 63 | 64 | - - - 65 | 66 | ## 0.9.1 (2016-03-31) 67 | * Fixed relative path issue with dropdown menu component 68 | 69 | - - - 70 | 71 | ## 0.9.0 (2016-03-30) 72 | * Support loading utility classes into "edx" namespace if RequireJS not available 73 | * Make StringUtils available as a context parameter to templates 74 | * Add Coveralls support 75 | * Fix Karma build failures on Travis 76 | 77 | - - - 78 | 79 | ## 0.8.0 (2016-03-22) 80 | * Add more HtmlUtils APIs 81 | 82 | - - - 83 | 84 | ## 0.7.0 (2016-03-22) 85 | * Dropdown Menu improved a11y to trap focus in the dropdown for 86 | * tab navigation and added a11y test coverage 87 | * Dropdown Menu updated to utilize Pattern Library classes for styling 88 | * and can add Pattern Library icons by passing an icon class via the model 89 | 90 | - - - 91 | 92 | ## 0.6.0 (2016-03-16) 93 | * Remove relative paths for use in edx-platform 94 | * Replace HtmlSnippet with a string marked as a snippet 95 | 96 | - - - 97 | 98 | ## 0.5.0 (2016-03-16) 99 | * Ensure sinon is distributed for ajax-helpers.js 100 | * Implement new HtmlUtils helper functions 101 | * Added image and placeholder logic to user anchor element 102 | * Added support for left and right arrow keys in the dropdown menu view 103 | 104 | - - - 105 | 106 | ## 0.4.0 (2016-03-09) 107 | * Add `getActiveFilterFields` method to the `PagingCollection` 108 | 109 | - - - 110 | 111 | ## 0.3.0 (2016-03-01) 112 | * Reorganize file structure for npm package 113 | 114 | - - - 115 | 116 | ## 0.2.0 (2016-02-29) 117 | * Packaged UI Toolkit for npm 118 | 119 | - - - 120 | 121 | ## 0.1.0 (2015-09-17) 122 | * Intitial release 123 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | gem 'github-pages' 3 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activesupport (7.0.7.2) 5 | concurrent-ruby (~> 1.0, >= 1.0.2) 6 | i18n (>= 1.6, < 2) 7 | minitest (>= 5.1) 8 | tzinfo (~> 2.0) 9 | addressable (2.8.1) 10 | public_suffix (>= 2.0.2, < 6.0) 11 | coffee-script (2.4.1) 12 | coffee-script-source 13 | execjs 14 | coffee-script-source (1.11.1) 15 | colorator (1.1.0) 16 | commonmarker (0.23.10) 17 | concurrent-ruby (1.2.2) 18 | dnsruby (1.61.9) 19 | simpleidn (~> 0.1) 20 | em-websocket (0.5.3) 21 | eventmachine (>= 0.12.9) 22 | http_parser.rb (~> 0) 23 | ethon (0.16.0) 24 | ffi (>= 1.15.0) 25 | eventmachine (1.2.7) 26 | execjs (2.8.1) 27 | faraday (2.7.4) 28 | faraday-net_http (>= 2.0, < 3.1) 29 | ruby2_keywords (>= 0.0.4) 30 | faraday-net_http (3.0.2) 31 | ffi (1.15.5) 32 | forwardable-extended (2.6.0) 33 | gemoji (3.0.1) 34 | github-pages (228) 35 | github-pages-health-check (= 1.17.9) 36 | jekyll (= 3.9.3) 37 | jekyll-avatar (= 0.7.0) 38 | jekyll-coffeescript (= 1.1.1) 39 | jekyll-commonmark-ghpages (= 0.4.0) 40 | jekyll-default-layout (= 0.1.4) 41 | jekyll-feed (= 0.15.1) 42 | jekyll-gist (= 1.5.0) 43 | jekyll-github-metadata (= 2.13.0) 44 | jekyll-include-cache (= 0.2.1) 45 | jekyll-mentions (= 1.6.0) 46 | jekyll-optional-front-matter (= 0.3.2) 47 | jekyll-paginate (= 1.1.0) 48 | jekyll-readme-index (= 0.3.0) 49 | jekyll-redirect-from (= 0.16.0) 50 | jekyll-relative-links (= 0.6.1) 51 | jekyll-remote-theme (= 0.4.3) 52 | jekyll-sass-converter (= 1.5.2) 53 | jekyll-seo-tag (= 2.8.0) 54 | jekyll-sitemap (= 1.4.0) 55 | jekyll-swiss (= 1.0.0) 56 | jekyll-theme-architect (= 0.2.0) 57 | jekyll-theme-cayman (= 0.2.0) 58 | jekyll-theme-dinky (= 0.2.0) 59 | jekyll-theme-hacker (= 0.2.0) 60 | jekyll-theme-leap-day (= 0.2.0) 61 | jekyll-theme-merlot (= 0.2.0) 62 | jekyll-theme-midnight (= 0.2.0) 63 | jekyll-theme-minimal (= 0.2.0) 64 | jekyll-theme-modernist (= 0.2.0) 65 | jekyll-theme-primer (= 0.6.0) 66 | jekyll-theme-slate (= 0.2.0) 67 | jekyll-theme-tactile (= 0.2.0) 68 | jekyll-theme-time-machine (= 0.2.0) 69 | jekyll-titles-from-headings (= 0.5.3) 70 | jemoji (= 0.12.0) 71 | kramdown (= 2.3.2) 72 | kramdown-parser-gfm (= 1.1.0) 73 | liquid (= 4.0.4) 74 | mercenary (~> 0.3) 75 | minima (= 2.5.1) 76 | nokogiri (>= 1.13.6, < 2.0) 77 | rouge (= 3.26.0) 78 | terminal-table (~> 1.4) 79 | github-pages-health-check (1.17.9) 80 | addressable (~> 2.3) 81 | dnsruby (~> 1.60) 82 | octokit (~> 4.0) 83 | public_suffix (>= 3.0, < 5.0) 84 | typhoeus (~> 1.3) 85 | html-pipeline (2.14.3) 86 | activesupport (>= 2) 87 | nokogiri (>= 1.4) 88 | http_parser.rb (0.8.0) 89 | i18n (1.14.1) 90 | concurrent-ruby (~> 1.0) 91 | jekyll (3.9.3) 92 | addressable (~> 2.4) 93 | colorator (~> 1.0) 94 | em-websocket (~> 0.5) 95 | i18n (>= 0.7, < 2) 96 | jekyll-sass-converter (~> 1.0) 97 | jekyll-watch (~> 2.0) 98 | kramdown (>= 1.17, < 3) 99 | liquid (~> 4.0) 100 | mercenary (~> 0.3.3) 101 | pathutil (~> 0.9) 102 | rouge (>= 1.7, < 4) 103 | safe_yaml (~> 1.0) 104 | jekyll-avatar (0.7.0) 105 | jekyll (>= 3.0, < 5.0) 106 | jekyll-coffeescript (1.1.1) 107 | coffee-script (~> 2.2) 108 | coffee-script-source (~> 1.11.1) 109 | jekyll-commonmark (1.4.0) 110 | commonmarker (~> 0.22) 111 | jekyll-commonmark-ghpages (0.4.0) 112 | commonmarker (~> 0.23.7) 113 | jekyll (~> 3.9.0) 114 | jekyll-commonmark (~> 1.4.0) 115 | rouge (>= 2.0, < 5.0) 116 | jekyll-default-layout (0.1.4) 117 | jekyll (~> 3.0) 118 | jekyll-feed (0.15.1) 119 | jekyll (>= 3.7, < 5.0) 120 | jekyll-gist (1.5.0) 121 | octokit (~> 4.2) 122 | jekyll-github-metadata (2.13.0) 123 | jekyll (>= 3.4, < 5.0) 124 | octokit (~> 4.0, != 4.4.0) 125 | jekyll-include-cache (0.2.1) 126 | jekyll (>= 3.7, < 5.0) 127 | jekyll-mentions (1.6.0) 128 | html-pipeline (~> 2.3) 129 | jekyll (>= 3.7, < 5.0) 130 | jekyll-optional-front-matter (0.3.2) 131 | jekyll (>= 3.0, < 5.0) 132 | jekyll-paginate (1.1.0) 133 | jekyll-readme-index (0.3.0) 134 | jekyll (>= 3.0, < 5.0) 135 | jekyll-redirect-from (0.16.0) 136 | jekyll (>= 3.3, < 5.0) 137 | jekyll-relative-links (0.6.1) 138 | jekyll (>= 3.3, < 5.0) 139 | jekyll-remote-theme (0.4.3) 140 | addressable (~> 2.0) 141 | jekyll (>= 3.5, < 5.0) 142 | jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) 143 | rubyzip (>= 1.3.0, < 3.0) 144 | jekyll-sass-converter (1.5.2) 145 | sass (~> 3.4) 146 | jekyll-seo-tag (2.8.0) 147 | jekyll (>= 3.8, < 5.0) 148 | jekyll-sitemap (1.4.0) 149 | jekyll (>= 3.7, < 5.0) 150 | jekyll-swiss (1.0.0) 151 | jekyll-theme-architect (0.2.0) 152 | jekyll (> 3.5, < 5.0) 153 | jekyll-seo-tag (~> 2.0) 154 | jekyll-theme-cayman (0.2.0) 155 | jekyll (> 3.5, < 5.0) 156 | jekyll-seo-tag (~> 2.0) 157 | jekyll-theme-dinky (0.2.0) 158 | jekyll (> 3.5, < 5.0) 159 | jekyll-seo-tag (~> 2.0) 160 | jekyll-theme-hacker (0.2.0) 161 | jekyll (> 3.5, < 5.0) 162 | jekyll-seo-tag (~> 2.0) 163 | jekyll-theme-leap-day (0.2.0) 164 | jekyll (> 3.5, < 5.0) 165 | jekyll-seo-tag (~> 2.0) 166 | jekyll-theme-merlot (0.2.0) 167 | jekyll (> 3.5, < 5.0) 168 | jekyll-seo-tag (~> 2.0) 169 | jekyll-theme-midnight (0.2.0) 170 | jekyll (> 3.5, < 5.0) 171 | jekyll-seo-tag (~> 2.0) 172 | jekyll-theme-minimal (0.2.0) 173 | jekyll (> 3.5, < 5.0) 174 | jekyll-seo-tag (~> 2.0) 175 | jekyll-theme-modernist (0.2.0) 176 | jekyll (> 3.5, < 5.0) 177 | jekyll-seo-tag (~> 2.0) 178 | jekyll-theme-primer (0.6.0) 179 | jekyll (> 3.5, < 5.0) 180 | jekyll-github-metadata (~> 2.9) 181 | jekyll-seo-tag (~> 2.0) 182 | jekyll-theme-slate (0.2.0) 183 | jekyll (> 3.5, < 5.0) 184 | jekyll-seo-tag (~> 2.0) 185 | jekyll-theme-tactile (0.2.0) 186 | jekyll (> 3.5, < 5.0) 187 | jekyll-seo-tag (~> 2.0) 188 | jekyll-theme-time-machine (0.2.0) 189 | jekyll (> 3.5, < 5.0) 190 | jekyll-seo-tag (~> 2.0) 191 | jekyll-titles-from-headings (0.5.3) 192 | jekyll (>= 3.3, < 5.0) 193 | jekyll-watch (2.2.1) 194 | listen (~> 3.0) 195 | jemoji (0.12.0) 196 | gemoji (~> 3.0) 197 | html-pipeline (~> 2.2) 198 | jekyll (>= 3.0, < 5.0) 199 | kramdown (2.3.2) 200 | rexml 201 | kramdown-parser-gfm (1.1.0) 202 | kramdown (~> 2.0) 203 | liquid (4.0.4) 204 | listen (3.8.0) 205 | rb-fsevent (~> 0.10, >= 0.10.3) 206 | rb-inotify (~> 0.9, >= 0.9.10) 207 | mercenary (0.3.6) 208 | mini_portile2 (2.8.1) 209 | minima (2.5.1) 210 | jekyll (>= 3.5, < 5.0) 211 | jekyll-feed (~> 0.9) 212 | jekyll-seo-tag (~> 2.1) 213 | minitest (5.19.0) 214 | nokogiri (1.14.3) 215 | mini_portile2 (~> 2.8.0) 216 | racc (~> 1.4) 217 | octokit (4.25.1) 218 | faraday (>= 1, < 3) 219 | sawyer (~> 0.9) 220 | pathutil (0.16.2) 221 | forwardable-extended (~> 2.6) 222 | public_suffix (4.0.7) 223 | racc (1.6.2) 224 | rb-fsevent (0.11.2) 225 | rb-inotify (0.10.1) 226 | ffi (~> 1.0) 227 | rexml (3.2.8) 228 | strscan (>= 3.0.9) 229 | rouge (3.26.0) 230 | ruby2_keywords (0.0.5) 231 | rubyzip (2.3.2) 232 | safe_yaml (1.0.5) 233 | sass (3.7.4) 234 | sass-listen (~> 4.0.0) 235 | sass-listen (4.0.0) 236 | rb-fsevent (~> 0.9, >= 0.9.4) 237 | rb-inotify (~> 0.9, >= 0.9.7) 238 | sawyer (0.9.2) 239 | addressable (>= 2.3.5) 240 | faraday (>= 0.17.3, < 3) 241 | simpleidn (0.2.1) 242 | unf (~> 0.1.4) 243 | strscan (3.1.0) 244 | terminal-table (1.8.0) 245 | unicode-display_width (~> 1.1, >= 1.1.1) 246 | typhoeus (1.4.0) 247 | ethon (>= 0.9.0) 248 | tzinfo (2.0.6) 249 | concurrent-ruby (~> 1.0) 250 | unf (0.1.4) 251 | unf_ext 252 | unf_ext (0.0.8.2) 253 | unicode-display_width (1.8.0) 254 | 255 | PLATFORMS 256 | ruby 257 | 258 | DEPENDENCIES 259 | github-pages 260 | 261 | BUNDLED WITH 262 | 1.17.2 263 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # edX UI Toolkit 2 | 3 | A JavaScript toolkit for building edX user interfaces. 4 | 5 | [![GitHub version](https://badge.fury.io/gh/edx%2Fedx-ui-toolkit.svg)](https://badge.fury.io/gh/edx%2Fedx-ui-toolkit) 6 | [![npm version](https://badge.fury.io/js/edx-ui-toolkit.svg)](https://badge.fury.io/js/edx-ui-toolkit) 7 | [![Bower version](https://badge.fury.io/bo/edx-ui-toolkit.svg)](https://badge.fury.io/bo/edx-ui-toolkit) 8 | [![CoverageStatus](https://img.shields.io/coveralls/edx/edx-ui-toolkit.svg)](https://coveralls.io/r/edx/edx-ui-toolkit?branch=master) 9 | 10 | - - - 11 | 12 | ## Table of Contents 13 | 14 | 1. [Overview](#overview) 15 | 2. [License](#license) 16 | 3. [Contributions](#contributions) 17 | 5. [Getting Started](#getting-started) 18 | 6. [Linting](#linting) 19 | 7. [Tests](#tests) 20 | 8. [Documentation](#documentation) 21 | 22 | - - - 23 | 24 | ## Overview 25 | 26 | This library contains the following: 27 | 28 | * Backbone views to implement patterns as defined by the edX Pattern Library: http://ux.edx.org/ 29 | * Utility methods to simplify the creation and testing of user interfaces 30 | 31 | ## License 32 | 33 | The code in this repository uses the Apache 2.0 license unless otherwise 34 | noted. Please see the [LICENSE file](https://github.com/openedx/edx-ui-toolkit/blob/master/LICENSE) for details. 35 | 36 | ## Contributions 37 | 38 | Contributions are very welcome. The easiest way is to fork this repo, and then 39 | make a pull request from your fork. The first time you make a pull request, you 40 | may be asked to sign a Contributor Agreement. 41 | 42 | Please refer to our [contributor guidelines](https://github.com/openedx/edx-ui-toolkit/blob/master/CONTRIBUTING.md) 43 | for important additional information. 44 | 45 | ## Getting Started 46 | 47 | The UI Toolkit uses Node 18 to manage its dependencies. To work with the repo locally, you will need to have Node 18 installed. We recommend [using n](https://github.com/tj/n) to manage node versions on your machine. 48 | 49 | 1. Get the code (e.g. clone the repository). 50 | 2. Install the Node requirements: 51 | 52 | $ npm i 53 | 54 | ## Linting 55 | 56 | $ gulp lint 57 | 58 | ## Tests 59 | 60 | To run tests in headless mode: 61 | 62 | $ gulp test 63 | 64 | To run tests in debug mode: 65 | 66 | $ gulp test-debug 67 | 68 | Once tests are running in debug mode, open this URL: 69 | 70 | http://localhost:9009/debug.html 71 | 72 | ## Documentation 73 | 74 | The UI Toolkit has auto-generated documentation available here: [http://ui-toolkit.edx.org/](http://ui-toolkit.edx.org/). 75 | 76 | To generate this documentation, run the following command: 77 | 78 | $ gulp doc 79 | 80 | To upload a preview of the documentation to S3: 81 | 82 | $ gulp preview 83 | 84 | To update the hosted documentation: 85 | 86 | $ gulp doc-publish 87 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # UI Toolkit Jekyll configuration 2 | 3 | title: edX UI Toolkit 4 | short_title: UI Toolkit 5 | tagline: A JavaScript library for building edX front end applications. 6 | 7 | author: 8 | name: edX UI Toolkit Team 9 | url: http://www.edx.org 10 | email: npmjs@edx.org 11 | 12 | url: http://ui-toolkit.edx.org 13 | 14 | baseurl: / 15 | permalink: /:categories/:title/ 16 | 17 | source: ./doc 18 | 19 | exclude: [ 20 | "bower.json", 21 | "doc", 22 | "Gemfile", 23 | "Gemfile.lock", 24 | "gulp", 25 | "gulpfile.js", 26 | "node_modules", 27 | "package.json" 28 | ] 29 | 30 | defaults: 31 | - 32 | scope: 33 | path: "" 34 | type: "testing" 35 | values: 36 | layout: "default" 37 | - 38 | scope: 39 | path: "" 40 | type: "utilities" 41 | values: 42 | layout: "default" 43 | - 44 | scope: 45 | path: "" 46 | type: "views" 47 | values: 48 | layout: "default" 49 | 50 | collections: 51 | testing: 52 | output: true 53 | utilities: 54 | output: true 55 | views: 56 | output: true 57 | 58 | markdown: kramdown 59 | highlighter: rouge 60 | 61 | port: 5000 62 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edx-ui-toolkit", 3 | "version": "1.5.3", 4 | "homepage": "https://github.com/openedx/edx-ui-toolkit", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/openedx/edx-ui-toolkit.git" 8 | }, 9 | "ignore": [ 10 | "**\/.*", 11 | "bower_components", 12 | "gulp/tasks", 13 | "node_modules", 14 | "test" 15 | ], 16 | "license": "Apache 2.0", 17 | "dependencies": { 18 | "backbone": "~1.2.3", 19 | "backbone.paginator": "~2.0.3", 20 | "jquery": "~2.2.0", 21 | "requirejs": "~2.1.22", 22 | "sinon": "~1.17.3", 23 | "text": "~2.0.12", 24 | "underscore": "~1.8.3", 25 | "urijs": "~1.16.1", 26 | "moment": "^2.15.1", 27 | "moment-timezone": "~0.5.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /catalog-info.yaml: -------------------------------------------------------------------------------- 1 | # This file records information about this repo. Its use is described in OEP-55: 2 | # https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html 3 | 4 | apiVersion: backstage.io/v1alpha1 5 | kind: Component 6 | metadata: 7 | # (Required) Must be the name of the repo, without the owning organization. 8 | name: 'edx-ui-toolkit' 9 | spec: 10 | owner: user:kdmccormick 11 | type: 'library' 12 | lifecycle: 'deprecated' 13 | dependencyOf: 14 | - 'edx-platform' 15 | - 'edx-ora2' 16 | -------------------------------------------------------------------------------- /doc/404.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: "Page not found" 4 | permalink: 404.html 5 | --- 6 | 7 |
8 | 19 |
20 | -------------------------------------------------------------------------------- /doc/CNAME: -------------------------------------------------------------------------------- 1 | ui-toolkit.edx.org 2 | -------------------------------------------------------------------------------- /doc/_data/edx-pattern-library.yml: -------------------------------------------------------------------------------- 1 | # edX Pattern Library Documentation Site: UXPL Config 2 | 3 | name: edX Pattern Library 4 | 5 | description: The (working) Visual, UI, and Front End Styleguide for edX 6 | 7 | url_github: https://github.com/edx/ux-pattern-library 8 | 9 | url_site: http://ux.edx.org 10 | url_documentation: https://github.com/edx/ux-pattern-library/wiki/ 11 | url_project: https://openedx.atlassian.net/projects/UXPL/issues 12 | 13 | path_styles: /blob/master/pattern-library/sass/ 14 | 15 | -------------------------------------------------------------------------------- /doc/_data/edx-ui-toolkit.yml: -------------------------------------------------------------------------------- 1 | # edX Pattern Library Documentation Site: UI Toolkit Config 2 | 3 | name: edX UI Toolkit 4 | 5 | description: 6 | 7 | url_github: https://github.com/openedx/edx-ui-toolkit 8 | 9 | url_site: 10 | url_documentation: https://github.com/openedx/edx-ui-toolkit/blob/master/README.md 11 | 12 | path_components: /blob/master/src/js/ 13 | -------------------------------------------------------------------------------- /doc/_includes/assets-primary.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/_includes/assets-secondary.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/_includes/collection-page-list.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /doc/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /doc/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {% if page.title == "Home" %} 11 | {{ site.title }} · {{ site.tagline }} 12 | {% else %} 13 | {{ page.title }} · {{ site.title }} 14 | {% endif %} 15 | 16 | 17 | 18 | 19 | {% include assets-primary.html %} 20 | 21 | -------------------------------------------------------------------------------- /doc/_includes/header.html: -------------------------------------------------------------------------------- 1 |
2 | 15 |
16 | -------------------------------------------------------------------------------- /doc/_includes/navigation-item.html: -------------------------------------------------------------------------------- 1 |
  • 2 | {% unless include.url == page.url %} 3 | 4 | {% endunless %} 5 | {{ include.title }} 6 | {% unless include.url == page.url %} 7 | 8 | {% endunless %} 9 |
  • 10 | -------------------------------------------------------------------------------- /doc/_includes/navigation.html: -------------------------------------------------------------------------------- 1 | 7 | 8 | {% assign sorted_utilities = site.utilities | sort: 'title' %} 9 | {% include collection-page-list.html title="Utilities" pages=sorted_utilities%} 10 | 11 | {% assign sorted_views = site.views | sort: 'title' %} 12 | {% include collection-page-list.html title="Views" pages=sorted_views%} 13 | 14 | {% assign sorted_test_helpers = site.testing | sort: 'title' %} 15 | {% include collection-page-list.html title="Testing" pages=sorted_test_helpers%} 16 | -------------------------------------------------------------------------------- /doc/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head.html %} 5 | 6 | 7 | 10 | 11 |
    12 | 13 |
    14 | {% include header.html %} 15 | 16 |
    17 |
    18 |
    19 | {% include navigation.html %} 20 |
    21 | 22 |
    23 | 24 |
    25 |
    26 | {% if page.githubPath %} 27 | 32 | {% endif %} 33 | 34 | {{ content }} 35 |
    36 |
    37 |
    38 |
    39 | 40 | {% include footer.html %} 41 |
    42 | 43 | {% include assets-secondary.html %} 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /doc/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openedx/edx-ui-toolkit/9ad092d0252096bad034f8d7c7196edf1fd864ce/doc/favicon.ico -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Home 4 | viewClass: index 5 | --- 6 | 7 | The edX UI Toolkit is a JavaScript library for building edX web applications. It consists of: 8 | 9 | * Backbone views to implement patterns as defined by the [edX Pattern Library](http://ux.edx.org). 10 | * Utility methods to simplify the creation and testing of user interfaces. 11 | 12 | The toolkit is an open source library that is hosted on [GitHub]({{ site.data.edx-ui-toolkit.url_github }}), 13 | and that can be installed as an [npm package](https://www.npmjs.com/package/edx-ui-toolkit). 14 | 15 | [![edX Pattern Library](https://nodei.co/npm/edx-ui-toolkit.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/edx-ui-toolkit/) 16 | 17 | You can read more on the [edX Front End Development wiki](https://openedx.atlassian.net/wiki/display/FEDX/). 18 | -------------------------------------------------------------------------------- /doc/static/images/edx-logo.svg: -------------------------------------------------------------------------------- 1 | edx-logo -------------------------------------------------------------------------------- /doc/static/js/ui-toolkit-doc-factory.js: -------------------------------------------------------------------------------- 1 | require( 2 | [ 3 | 'jquery' 4 | ], 5 | function() { 6 | 'use strict'; 7 | } 8 | ); 9 | -------------------------------------------------------------------------------- /doc/static/sass/_build.scss: -------------------------------------------------------------------------------- 1 | // UI Toolkit styles builder 2 | 3 | @import 'bourbon/app/assets/stylesheets/bourbon'; 4 | @import 'susy/sass/susy'; 5 | 6 | @import 'utilities'; 7 | @import 'components'; 8 | @import 'layouts'; 9 | @import 'syntax'; 10 | -------------------------------------------------------------------------------- /doc/static/sass/_components.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------ 2 | // edX Pattern Library Site: Components 3 | 4 | // About: styling for specific UI components ranging from global to modular. 5 | 6 | // #CONTROLS 7 | // #SYSTEM FEEDBACK 8 | // #HEADER 9 | // #NAVIGATION 10 | // #SIDEBAR 11 | // #MAIN CONTENT 12 | // #FOOTER 13 | // #PATTERNS 14 | // #SPECIFIC PATTERNS 15 | // #EXAMPLE - GRID 16 | // #EXAMPLE - LAYOUTS 17 | 18 | 19 | // ------------------------------ 20 | // #CONTROLS 21 | // ------------------------------ 22 | 23 | // tab panels and examples 24 | .example-set { 25 | @extend %clear-last-child; 26 | @include clearfix(); 27 | padding-bottom: spacing-vertical(base); 28 | border-bottom: 2rem solid palette(grayscale-cool, x-light); 29 | margin-bottom: spacing-vertical(base); 30 | 31 | .button-overlay-demo { 32 | @include linear-gradient(50deg, palette(primary, accent) 0%, palette(primary, base) 100%); 33 | padding: spacing-vertical(mid-large) spacing-horizontal(mid-large); 34 | } 35 | } 36 | 37 | // copyable patterns 38 | .is-copyable { 39 | display: inline-block; 40 | white-space: nowrap; 41 | font-family: $font-family-monospace; 42 | } 43 | 44 | // ------------------------------ 45 | // #SYSTEM FEEDBACK 46 | // ------------------------------ 47 | .badge { 48 | @include text-align(center); 49 | border-radius: 15rem; 50 | padding: spacing-vertical(xxx-small) spacing-horizontal(small); 51 | font-size: font-size(xxx-small); 52 | font-weight: font-weight(bold); 53 | text-transform: uppercase; 54 | white-space: nowrap; 55 | letter-spacing: letter-spacing(loose); 56 | 57 | .badge-abbreviation { 58 | border-bottom: none; 59 | } 60 | } 61 | 62 | // ------------------------------ 63 | // #HEADER 64 | // ------------------------------ 65 | .doc-wrapper-header-site { 66 | background: palette(grayscale, white); 67 | } 68 | 69 | 70 | .doc-header-site { 71 | 72 | } 73 | 74 | // ------------------------------ 75 | // #NAVIGATION 76 | // ------------------------------ 77 | // navigation - sidebar-based 78 | 79 | %spaced-nav { 80 | @include padding-left(spacing-horizontal(x-small)); 81 | @include padding-right(spacing-horizontal(x-small)); 82 | } 83 | 84 | .doc-nav { 85 | padding-bottom: spacing-vertical(small); 86 | 87 | &:last-child { 88 | padding-bottom: 0; 89 | } 90 | 91 | .doc-link { 92 | // STATE: hover, active, and focus 93 | &:hover, 94 | &:active, 95 | &:focus { 96 | 97 | } 98 | 99 | // CASE: item is current 100 | &.is-current { 101 | 102 | .doc-link:before { 103 | display: inline-block; 104 | } 105 | } 106 | } 107 | 108 | // CASE: pattern listing 109 | .list-patterns { 110 | 111 | .pattern { 112 | @include clearfix(); 113 | 114 | .pattern-title, 115 | .badge { 116 | display: inline-block; 117 | } 118 | 119 | .pattern-title { 120 | width: 80%; 121 | } 122 | 123 | .badge { 124 | @include float(right); 125 | } 126 | } 127 | } 128 | 129 | .doc-nav-title { 130 | @extend %spaced-nav; 131 | text-transform: uppercase; 132 | color: palette(primary, x-dark); 133 | font-size: font-size(small); 134 | } 135 | 136 | .item { 137 | @extend %spaced-nav; 138 | display: inline-block; 139 | 140 | @include susy-media($bp-screen-md) { 141 | display: block; 142 | } 143 | 144 | .doc-link { 145 | display: block; 146 | width: 100%; 147 | } 148 | } 149 | 150 | .is-current { 151 | color: palette(primary, dark); 152 | background-color: palette(grayscale-cool, x-trans); 153 | } 154 | 155 | } 156 | 157 | // internal site navigation 158 | .doc-nav-internal { 159 | 160 | } 161 | 162 | // external site navigation 163 | .doc-nav-external { 164 | 165 | } 166 | 167 | .doc-tab { 168 | 169 | &:focus { 170 | outline: none; 171 | } 172 | } 173 | 174 | .doc-tab-content { 175 | @extend %depth-2; 176 | @include transition(box-shadow timing(fast) ease-in-out); 177 | border: rem(1) solid palette(grayscale, x-light); 178 | padding: spacing-vertical(base) spacing-horizontal(base); 179 | 180 | @include susy-breakpoint($bp-screen-md, $susy-defaults) { 181 | padding: spacing-vertical(base) spacing-horizontal(mid-large); 182 | } 183 | } 184 | 185 | .doc-settings { 186 | background: palette(grayscale, xx-light); 187 | margin-bottom: spacing-vertical(large); 188 | padding: spacing-vertical(small) spacing-horizontal(small); 189 | } 190 | 191 | .doc-setting { 192 | display: inline-block; 193 | vertical-align: middle; 194 | @include margin-right(spacing-horizontal(small)); 195 | 196 | &:last-child { 197 | @include margin-right(0); 198 | } 199 | } 200 | 201 | // ------------------------------ 202 | // #SIDEBAR 203 | // ------------------------------ 204 | .doc-wrapper-content-supplemental { 205 | 206 | } 207 | 208 | // ------------------------------ 209 | // #MAIN CONTENT 210 | // ------------------------------ 211 | .doc-wrapper-content-main { 212 | 213 | } 214 | 215 | // ------------------------------ 216 | // #FOOTER 217 | // ------------------------------ 218 | .doc-footer-site { 219 | @extend .grid-container; 220 | } 221 | 222 | .doc-footer-site-copyright { 223 | 224 | .doc-copy { 225 | font-size: font-size(small); 226 | margin: 0; 227 | } 228 | } 229 | 230 | // ------------------------------ 231 | // #PATTERNS 232 | // ------------------------------ 233 | .doc-pattern { 234 | 235 | .example-set-hd { 236 | margin-bottom: spacing-vertical(small); 237 | } 238 | } 239 | 240 | .doc-pattern-header { 241 | margin-bottom: spacing-vertical(small); 242 | padding-bottom: spacing-vertical(small); 243 | } 244 | 245 | 246 | // pattern info tabs 247 | .doc-tab-labels { 248 | @include clearfix(); 249 | @extend %reset-lists; 250 | border-bottom: $doc-tab-label-height solid palette(grayscale, x-light); 251 | } 252 | 253 | .doc-tab-heading { 254 | @extend %a11y-hide; 255 | } 256 | 257 | .doc-tab-label { 258 | @include float(left); 259 | position: relative; 260 | bottom: -(spacing-vertical(xx-small)); 261 | margin-bottom: 0; 262 | 263 | .doc-link { 264 | display: block; 265 | margin: 0; 266 | border-width: 0 0 $doc-tab-label-height 0; 267 | border-color: $transparent; 268 | border-style: solid; 269 | background: $transparent; 270 | padding: spacing-vertical(x-small) spacing-horizontal(mid-small); 271 | font-weight: font-weight(semi-bold); 272 | color: palette(grayscale, dark); 273 | 274 | &:hover, 275 | &:focus { 276 | border-bottom-color: palette(primary, accent); 277 | color: palette(primary, accent); 278 | } 279 | 280 | &.is-active { 281 | border-bottom-color: palette(grayscale, x-dark); 282 | color: palette(grayscale, x-dark); 283 | } 284 | } 285 | } 286 | 287 | 288 | // ------------------------------ 289 | // #EXAMPLE - LAYOUTS 290 | // ------------------------------ 291 | %demo-layout-column { 292 | background: $grid-debug-color; 293 | border: 1px solid saturate($grid-debug-color, 100%); 294 | 295 | @include susy-breakpoint($bp-screen-md, $susy-defaults) { 296 | background: saturate($grid-debug-color, 33%); 297 | } 298 | 299 | @include susy-breakpoint($bp-screen-lg, $susy-defaults) { 300 | background: saturate($grid-debug-color, 66%); 301 | } 302 | 303 | @include susy-breakpoint($bp-screen-xl, $susy-defaults) { 304 | background: saturate($grid-debug-color, 100%); 305 | } 306 | } 307 | 308 | .example-layout { 309 | 310 | .layout-col, 311 | .layout-view-col 312 | { 313 | @extend %demo-layout-column; 314 | padding: spacing-vertical(small) spacing-horizontal(small); 315 | } 316 | 317 | .example-label { 318 | @extend %copy-micro; 319 | display: block; 320 | color: palette(grayscale, black-t); 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /doc/static/sass/_layouts.scss: -------------------------------------------------------------------------------- 1 | // UI Toolkit layouts 2 | 3 | .doc-wrapper-header-site, .doc-wrapper-footer-site { 4 | @extend %grid-wrapper; 5 | } 6 | 7 | .doc-header-site { 8 | @extend .grid-container; 9 | padding-top: spacing-vertical(x-small); 10 | } 11 | 12 | .doc-header-site-title { 13 | padding: 0 spacing-vertical(small); 14 | margin-bottom: 0; 15 | font-weight: font-weight(bold); 16 | white-space: nowrap; 17 | 18 | .doc-header-logo { 19 | @include float(left); 20 | height: 40px; 21 | margin-right: spacing-horizontal(small); 22 | } 23 | } 24 | 25 | // content 26 | .doc-wrapper-content { 27 | @extend %grid-wrapper; 28 | } 29 | 30 | .doc-content { 31 | @extend .grid-container; 32 | margin-top: spacing-vertical(x-small); 33 | padding-bottom: spacing-vertical(x-small); 34 | } 35 | 36 | .doc-links { 37 | @include float(right); 38 | } 39 | 40 | // Style the default content generated by Jekyll markup 41 | .doc-wrapper-content-main { 42 | padding-top: spacing-vertical(small); 43 | 44 | @include susy-breakpoint($bp-screen-md, $susy-defaults) { 45 | @include span(9 last); 46 | } 47 | 48 | h1 { 49 | @extend %hd-3; 50 | line-height: 1; 51 | color: palette(primary, x-dark); 52 | } 53 | 54 | h2 { 55 | @extend %hd-4; 56 | color: palette(primary, x-dark); 57 | } 58 | 59 | h3 { 60 | @extend %hd-5; 61 | color: palette(primary, x-dark); 62 | } 63 | 64 | strong { 65 | color: palette(primary, x-dark); 66 | font-weight: normal; 67 | } 68 | 69 | p { 70 | line-height: line-height(large); 71 | } 72 | 73 | .highlight { 74 | border: rem(2) solid palette(grayscale, base); 75 | padding: spacing-horizontal(small); 76 | background-color: palette(grayscale-cool, xx-trans); 77 | } 78 | 79 | code { 80 | font-family: $font-family-monospace; 81 | } 82 | } 83 | 84 | // sidebar 85 | .doc-wrapper-content-supplemental { 86 | background-color: palette(grayscale-cool, xx-light); 87 | padding: spacing-vertical(small); 88 | 89 | @include susy-breakpoint($bp-screen-md, $susy-defaults) { 90 | @include span(3 first); 91 | } 92 | } 93 | 94 | // pattern 95 | .doc-wrapper-pattern { 96 | border-bottom: rem(2) solid palette(grayscale, mid-dark); 97 | box-shadow: inset spacing-horizontal(xx-small) 0 0 0 $transparent; 98 | 99 | &:last-child { 100 | border-bottom: none; 101 | } 102 | } 103 | 104 | .doc-pattern { 105 | @extend %grid-row !optional; 106 | } 107 | 108 | .doc-pattern-header { 109 | @include clearfix(); 110 | } 111 | 112 | .doc-pattern-title { 113 | margin-bottom: spacing-vertical(x-small); 114 | font-size: font-size(xx-large); 115 | 116 | @include susy-breakpoint($bp-screen-xl, $susy-defaults) { 117 | @include span(6 first); 118 | margin-bottom: 0; 119 | } 120 | } 121 | 122 | .doc-pattern-meta { 123 | @include susy-breakpoint($bp-screen-xl, $susy-defaults) { 124 | @include span(6 last); 125 | @include text-align(right); 126 | } 127 | 128 | .meta-updated { 129 | margin-bottom: spacing-vertical(x-small); 130 | 131 | @include susy-breakpoint($bp-screen-md, $susy-defaults) { 132 | margin-bottom: 0; 133 | } 134 | } 135 | } 136 | 137 | 138 | // ------------------------------ 139 | // #GENERAL CANVAS LAYOUT 140 | // ------------------------------ 141 | .doc-view-example { 142 | // @extend %depth-0; 143 | 144 | .doc-wrapper-view-header { 145 | margin-bottom: spacing-vertical(base); 146 | padding: spacing-vertical(mid-small) gutter(); 147 | @extend %depth-1; 148 | } 149 | 150 | .doc-view-header { 151 | @include container(); 152 | } 153 | 154 | .example { 155 | margin-bottom: spacing-vertical(base); 156 | 157 | &:last-child { 158 | margin-bottom: 0; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /doc/static/sass/_syntax.scss: -------------------------------------------------------------------------------- 1 | .highlight { 2 | max-width: 100%; // Don't expand beyond the available space 3 | white-space: pre-wrap; // Wrap long lines 4 | } 5 | 6 | .highlight .hll { background-color: #ffc; } 7 | .highlight .c { color: #999; } /* Comment */ 8 | .highlight .err { color: #a00; background-color: #faa } /* Error */ 9 | .highlight .k { color: #069; } /* Keyword */ 10 | .highlight .o { color: #555 } /* Operator */ 11 | .highlight .cm { color: #09f; font-style: italic } /* Comment.Multiline */ 12 | .highlight .cp { color: #099 } /* Comment.Preproc */ 13 | .highlight .c1 { color: #999; } /* Comment.Single */ 14 | .highlight .cs { color: #999; } /* Comment.Special */ 15 | .highlight .gd { background-color: #fcc; border: 1px solid #c00 } /* Generic.Deleted */ 16 | .highlight .ge { font-style: italic } /* Generic.Emph */ 17 | .highlight .gr { color: #f00 } /* Generic.Error */ 18 | .highlight .gh { color: #030; } /* Generic.Heading */ 19 | .highlight .gi { background-color: #cfc; border: 1px solid #0c0 } /* Generic.Inserted */ 20 | .highlight .go { color: #aaa } /* Generic.Output */ 21 | .highlight .gp { color: #009; } /* Generic.Prompt */ 22 | .highlight .gs { } /* Generic.Strong */ 23 | .highlight .gu { color: #030; } /* Generic.Subheading */ 24 | .highlight .gt { color: #9c6 } /* Generic.Traceback */ 25 | .highlight .kc { color: #069; } /* Keyword.Constant */ 26 | .highlight .kd { color: #069; } /* Keyword.Declaration */ 27 | .highlight .kn { color: #069; } /* Keyword.Namespace */ 28 | .highlight .kp { color: #069 } /* Keyword.Pseudo */ 29 | .highlight .kr { color: #069; } /* Keyword.Reserved */ 30 | .highlight .kt { color: #078; } /* Keyword.Type */ 31 | .highlight .m { color: #f60 } /* Literal.Number */ 32 | .highlight .s { color: #d44950 } /* Literal.String */ 33 | .highlight .na { color: #4f9fcf } /* Name.Attribute */ 34 | .highlight .nb { color: #366 } /* Name.Builtin */ 35 | .highlight .nc { color: #0a8; } /* Name.Class */ 36 | .highlight .no { color: #360 } /* Name.Constant */ 37 | .highlight .nd { color: #99f } /* Name.Decorator */ 38 | .highlight .ni { color: #999; } /* Name.Entity */ 39 | .highlight .ne { color: #c00; } /* Name.Exception */ 40 | .highlight .nf { color: #c0f } /* Name.Function */ 41 | .highlight .nl { color: #99f } /* Name.Label */ 42 | .highlight .nn { color: #0cf; } /* Name.Namespace */ 43 | .highlight .nt { color: #2f6f9f; } /* Name.Tag */ 44 | .highlight .nv { color: #033 } /* Name.Variable */ 45 | .highlight .ow { color: #000; } /* Operator.Word */ 46 | .highlight .w { color: #bbb } /* Text.Whitespace */ 47 | .highlight .mf { color: #f60 } /* Literal.Number.Float */ 48 | .highlight .mh { color: #f60 } /* Literal.Number.Hex */ 49 | .highlight .mi { color: #f60 } /* Literal.Number.Integer */ 50 | .highlight .mo { color: #f60 } /* Literal.Number.Oct */ 51 | .highlight .sb { color: #c30 } /* Literal.String.Backtick */ 52 | .highlight .sc { color: #c30 } /* Literal.String.Char */ 53 | .highlight .sd { color: #c30; font-style: italic } /* Literal.String.Doc */ 54 | .highlight .s2 { color: #c30 } /* Literal.String.Double */ 55 | .highlight .se { color: #c30; } /* Literal.String.Escape */ 56 | .highlight .sh { color: #c30 } /* Literal.String.Heredoc */ 57 | .highlight .si { color: #a00 } /* Literal.String.Interpol */ 58 | .highlight .sx { color: #c30 } /* Literal.String.Other */ 59 | .highlight .sr { color: #3aa } /* Literal.String.Regex */ 60 | .highlight .s1 { color: #c30 } /* Literal.String.Single */ 61 | .highlight .ss { color: #fc3 } /* Literal.String.Symbol */ 62 | .highlight .bp { color: #366 } /* Name.Builtin.Pseudo */ 63 | .highlight .vc { color: #033 } /* Name.Variable.Class */ 64 | .highlight .vg { color: #033 } /* Name.Variable.Global */ 65 | .highlight .vi { color: #033 } /* Name.Variable.Instance */ 66 | .highlight .il { color: #f60 } /* Literal.Number.Integer.Long */ 67 | 68 | .css .o, 69 | .css .o + .nt, 70 | .css .nt + .nt { color: #999; } 71 | -------------------------------------------------------------------------------- /doc/static/sass/_utilities.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------ 2 | // edX Pattern Library Site: Utilities 3 | 4 | // About: configuration, functions, variables, mixins, and general helpers for rendering. 5 | 6 | // ---------------------------- 7 | // #COLORS 8 | // ---------------------------- 9 | // color: config 10 | $transparent: rgba(0, 0, 0, 0); 11 | $white: rgb(255, 255, 255); 12 | 13 | // shadows 14 | $shadow: rgba(0, 0, 0, .25); 15 | 16 | $text-emphasized-color: palette(grayscale, black) !default; 17 | $text-emphasized-font-weight: font-weight(normal) !default; 18 | $text-de-emphasized-color: palette(grayscale, dark) !default; 19 | $text-de-emphasized-font-weight: font-weight(normal) !default; 20 | 21 | $link-color: palette(primary, dark) !default; 22 | $link-focus-color: palette(primary, light) !default; 23 | $link-visited-color: palette(primary, dark) !default; 24 | 25 | // ------------------------------ 26 | // #VARIABLES 27 | // ------------------------------ 28 | // pl UI 29 | $text-base-color: palette(grayscale, x-dark) !default; 30 | $doc-tab-label-height: 4rem; // height of tab labels 31 | $doc-link-color: palette(primary, dark); 32 | $font-family-monospace: 'Bitstream Vera Sans Mono', Consolas, Courier, monospace !default; 33 | $grid-debug-color: rgba(203, 89, 141, 0.33) !default; 34 | $depth-0-bg: palette(grayscale, x-back) !default; // -0 depth level 35 | $depth-1-bg: $white !default; // +1 depth level 36 | $depth-2-bg: $white !default; // +2 depth level 37 | 38 | // grid - breakpoints 39 | $bp-screen-sm: 480px !default; 40 | $bp-screen-md: 768px !default; 41 | $bp-screen-lg: 1024px !default; 42 | $bp-screen-xl: 1280px !default; 43 | 44 | $grid-direction: row !default; // set to 'row-reverse' to reverse. Follows flexbox spec. 45 | $grid-max-width: rem(1170) !default; 46 | 47 | // ------------------------------ 48 | // #HELPERS 49 | // ------------------------------ 50 | 51 | %grid-wrapper { 52 | padding-right: spacing-horizontal(base); 53 | padding-left: spacing-horizontal(base); 54 | } 55 | 56 | // Wrap grids with grid-container. 57 | @mixin grid-container($direction:$grid-direction, $size:$grid-max-width) { 58 | display: flex; 59 | max-width: $size; 60 | flex-direction: $direction; 61 | flex-wrap: wrap; 62 | justify-content: flex-start; 63 | align-items: flex-start; 64 | box-sizing: border-box; 65 | margin: 0 auto; 66 | } 67 | 68 | .grid-container { 69 | @include grid-container; 70 | 71 | .col { 72 | &.col-first { 73 | @include margin-left(0); 74 | } 75 | 76 | &.col-last { 77 | @include margin-right(0); 78 | } 79 | } 80 | } 81 | 82 | 83 | // ---------------------------- 84 | // #UTILITY 85 | // ---------------------------- 86 | // accessibility 87 | %a11y-hide { 88 | // clip has been deprecated but is still supported 89 | clip: rect(1px 1px 1px 1px); 90 | clip: rect(1px, 1px, 1px, 1px); 91 | position: absolute; 92 | margin: -1px; 93 | height: 1px; 94 | width: 1px; 95 | border: 0; 96 | padding: 0; 97 | overflow: hidden; 98 | // ensure there are spaces in sr text 99 | word-wrap: normal; 100 | } 101 | 102 | // links: base 103 | %link { 104 | @include transition( 105 | color timing(xx-fast) ease-in-out 0s 106 | ); 107 | 108 | color: $link-color; 109 | text-decoration: none; 110 | 111 | // STATE: hover, active, focus 112 | &:hover, 113 | &:active, 114 | &:focus { 115 | color: $link-focus-color; 116 | text-decoration: underline; 117 | } 118 | 119 | // STATE: is disabled 120 | &:disabled, 121 | &.is-disabled { 122 | display: none; 123 | color: palette(grayscale, base); 124 | } 125 | 126 | // STATE: is pressed or active 127 | &:active, 128 | &.is-pressed, 129 | &.is-active { 130 | color: $link-focus-color; 131 | } 132 | } 133 | 134 | // links: text-based 135 | %link-text { 136 | @extend %link; 137 | @include transition( 138 | box-shadow timing(xx-fast) ease-in-out 0s 139 | ); 140 | 141 | box-shadow: 0 1px 0 0 $transparent; 142 | } 143 | 144 | %copy { 145 | color: $text-base-color; 146 | 147 | &.emphasized { 148 | @extend %copy-emphasized; 149 | } 150 | 151 | &.de-emphasized { 152 | @extend %copy-de-emphasized; 153 | } 154 | 155 | a { 156 | @extend %link-text; 157 | } 158 | } 159 | 160 | %copy-de-emphasized { 161 | color: $text-de-emphasized-color; 162 | font-weight: $text-de-emphasized-font-weight; 163 | } 164 | 165 | // broad stroke extend for emphasized/de-emphasized copy 166 | %copy-emphasized { 167 | color: $text-emphasized-color; 168 | font-weight: $text-emphasized-font-weight; 169 | } 170 | 171 | %copy-micro { 172 | @extend %copy; 173 | font-size: font-size(x-small); 174 | line-height: line-height(x-small); 175 | 176 | p, 177 | ol, 178 | ul, 179 | dl { 180 | margin-bottom: spacing-vertical(small); 181 | } 182 | } 183 | 184 | 185 | %depth-0 { 186 | background: $depth-0-bg; 187 | } 188 | 189 | %depth-1 { 190 | background: $depth-1-bg; 191 | } 192 | 193 | %depth-2 { 194 | background: $depth-2-bg; 195 | box-shadow: 0 1px 2px 1px $shadow; 196 | } 197 | 198 | %clear-last-child { 199 | 200 | &:last-child { 201 | margin-bottom: 0; 202 | padding-bottom: 0; 203 | border-bottom: none; 204 | } 205 | } 206 | 207 | // Level three heading 208 | %hd-3 { 209 | margin-bottom: spacing-vertical(x-small); 210 | font-size: font-size(xx-large); 211 | line-height: line-height(x-large); 212 | } 213 | 214 | // Level four heading 215 | %hd-4 { 216 | margin-bottom: spacing-vertical(x-small); 217 | font-size: font-size(x-large); 218 | line-height: line-height(large); 219 | } 220 | 221 | // Level five heading 222 | %hd-5 { 223 | margin-bottom: spacing-vertical(x-small); 224 | font-size: font-size(large); 225 | line-height: line-height(large); 226 | } 227 | 228 | %reset-lists { 229 | margin: 0; 230 | padding: 0; 231 | list-style: none; 232 | text-indent: 0; 233 | } 234 | 235 | 236 | // ------------------------------ 237 | // #BASE RESET 238 | // ------------------------------ 239 | // basic 240 | html, body { 241 | height: 100%; 242 | color: $text-base-color; 243 | } 244 | 245 | .doc { 246 | @extend %depth-0; 247 | background-color: palette(grayscale, white); 248 | } 249 | 250 | a { 251 | color: $doc-link-color; 252 | } 253 | -------------------------------------------------------------------------------- /doc/static/sass/main-ltr.scss: -------------------------------------------------------------------------------- 1 | // UI Toolkit main styles 2 | 3 | // Load LTR functions and mixins 4 | @import 'bi-app-sass/bi-app/bi-app-ltr'; 5 | 6 | // Load the shared build 7 | @import 'build'; 8 | -------------------------------------------------------------------------------- /doc/static/sass/main-rtl.scss: -------------------------------------------------------------------------------- 1 | // UI Toolkit main styles 2 | 3 | // Load RTL functions and mixins 4 | @import 'bi-app-sass/bi-app/bi-app-rtl'; 5 | 6 | // Load the shared build 7 | @import 'build'; 8 | -------------------------------------------------------------------------------- /gulp/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaFeatures": { 4 | "globalReturn": true 5 | } 6 | }, 7 | "rules": { 8 | "no-console": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gulp/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | documentation: { 5 | targetDir: './_site', 6 | previewTargetDir: './_preview_site', 7 | testing: { 8 | sources: [ 9 | './src/js/utils/spec-helpers/*.js' 10 | ], 11 | output: './doc/_testing', 12 | viewClass: 'testing' 13 | }, 14 | utilities: { 15 | sources: [ 16 | './src/js/utils/*.js' 17 | ], 18 | output: './doc/_utilities', 19 | viewClass: 'utility' 20 | }, 21 | views: { 22 | sources: [ 23 | './src/js/!(utils)/*.js' 24 | ], 25 | output: './doc/_views', 26 | viewClass: 'view' 27 | }, 28 | browserSync: { 29 | server: { 30 | baseDir: './_site' 31 | }, 32 | ui: { 33 | port: 5000, 34 | weinre: { 35 | port: 5002 36 | } 37 | } 38 | }, 39 | templates: [ 40 | './doc/*.md', 41 | './doc/_data/**/*', 42 | './doc/_layouts/**/*' 43 | ], 44 | static: [ 45 | './doc/static/**/*' 46 | ], 47 | gitHubPages: { 48 | files: './_site/**/*' 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /gulp/tasks/clean.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | config = require('../config'), 5 | del = require('del'); 6 | 7 | gulp.task('clean', function() { 8 | return del([ 9 | // Remove the Jekyll site 10 | config.documentation.targetDir, 11 | 12 | // Remove the preview site 13 | config.documentation.previewTargetDir, 14 | 15 | // Remove the JSDoc generated markdown 16 | config.documentation.testing.output, 17 | config.documentation.utilities.output, 18 | config.documentation.views.output 19 | ]); 20 | }); 21 | 22 | exports.clean = 'clean'; 23 | -------------------------------------------------------------------------------- /gulp/tasks/default.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | lint = require('./lint'), 5 | test = require('./test'), 6 | doc = require('./doc'); 7 | 8 | exports.default = gulp.series( 9 | lint.lint, 10 | test.test, 11 | doc.docBuild 12 | ); 13 | -------------------------------------------------------------------------------- /gulp/tasks/doc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gulp tasks for building API documentation for the UI Toolkit. 3 | * 4 | * The tasks are as follows: 5 | * - doc: builds the API documentation 6 | * - doc-serve: serves up the API documentation through browser sync 7 | */ 8 | 9 | 'use strict'; 10 | 11 | var gulp = require('gulp'), 12 | childProcess = require('child_process'), 13 | browserSync = require('browser-sync'), 14 | runSequence = require('gulp4-run-sequence'), 15 | config = require('../config').documentation, 16 | generateDoc = require('../utils/generate-doc'), 17 | rename = require('gulp-rename'), 18 | webpack = require('webpack'), 19 | webpackStream = require('webpack-stream'), 20 | ghPages = require('gulp-gh-pages'), 21 | webpackConfig = require('../../webpack.config.js'), 22 | clean = require('./clean'), 23 | renameAsMarkdown, 24 | generateDocFor; 25 | 26 | renameAsMarkdown = function(path) { 27 | var renamedPath = path; 28 | renamedPath.extname = '.md'; 29 | return renamedPath; 30 | }; 31 | 32 | generateDocFor = function(options) { 33 | var i, sources, 34 | sourceLength = options.sources.length; 35 | for (i = 0; i < sourceLength; i += 1) { 36 | sources = options.sources[i]; 37 | console.log('Generating documentation for ' + sources); 38 | gulp.src(sources, {buffer: false}) 39 | .pipe(generateDoc(options.viewClass)) 40 | .pipe(rename(renameAsMarkdown)) 41 | .pipe(gulp.dest(options.output)); 42 | } 43 | }; 44 | 45 | gulp.task('doc', function(callback) { 46 | runSequence( 47 | 'doc-build', 48 | 'doc-serve', 49 | callback 50 | ); 51 | }); 52 | 53 | gulp.task('doc-build', function(callback) { 54 | runSequence( 55 | ['doc-testing', 'doc-utils', 'doc-views'], 56 | 'webpack', 57 | 'jekyll-build', 58 | callback 59 | ); 60 | }); 61 | 62 | gulp.task('doc-serve', function(callback) { 63 | // Run browserSync to serve the doc site 64 | browserSync(config.browserSync); 65 | 66 | // Watch the UI Toolkit's source code 67 | gulp.watch(config.testing.sources, ['doc-testing', 'webpack-rebuild']); 68 | gulp.watch(config.utilities.sources, ['doc-utils', 'webpack-rebuild']); 69 | gulp.watch(config.views.sources, ['doc-views', 'webpack-rebuild']); 70 | 71 | // Watch the doc site's static assets 72 | gulp.watch(config.static, ['webpack-rebuild']); 73 | 74 | // Watch the doc site's templates 75 | gulp.watch(config.templates, ['jekyll-rebuild']); 76 | callback(); 77 | }); 78 | 79 | gulp.task('doc-testing', function(callback) { 80 | generateDocFor(config.testing); 81 | callback(); 82 | }); 83 | 84 | gulp.task('doc-utils', function(callback) { 85 | generateDocFor(config.utilities); 86 | callback(); 87 | }); 88 | 89 | gulp.task('doc-views', function(callback) { 90 | generateDocFor(config.views); 91 | callback(); 92 | }); 93 | 94 | gulp.task('webpack', function() { 95 | return gulp.src('.', {allowEmpty: true}) 96 | .pipe(webpackStream(webpackConfig, webpack)) 97 | .pipe(gulp.dest(webpackConfig.output.path)) 98 | .pipe(browserSync.stream()); 99 | }); 100 | 101 | gulp.task('webpack-rebuild', function(callback) { 102 | runSequence( 103 | 'webpack', 104 | 'jekyll-rebuild', 105 | callback 106 | ); 107 | }); 108 | 109 | gulp.task('jekyll-build', function(callback) { 110 | childProcess.execSync('jekyll build'); 111 | callback(); 112 | }); 113 | 114 | gulp.task('jekyll-rebuild', gulp.series('jekyll-build', function(callback) { 115 | browserSync.reload(); 116 | callback(); 117 | })); 118 | 119 | gulp.task('doc-publish', gulp.series(clean.clean, 'doc-build', function() { 120 | return gulp.src(config.gitHubPages.files) 121 | .pipe(ghPages()); 122 | })); 123 | 124 | exports.docBuild = gulp.series( 125 | gulp.parallel('doc-testing', 'doc-utils', 'doc-views'), 126 | 'webpack', 127 | 'jekyll-build' 128 | ); 129 | -------------------------------------------------------------------------------- /gulp/tasks/lint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | shell = require('gulp-shell'); 5 | 6 | gulp.task('lint', shell.task('eslint .')); 7 | 8 | exports.lint = 'lint'; 9 | -------------------------------------------------------------------------------- /gulp/tasks/preview.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Gulp tasks for managing an S3-based preview of the UI Toolkit documentation. 3 | * 4 | * The tasks are as follows: 5 | * - build-preview: builds a preview site and uploads it to S3 6 | * - jekyll-build-preview: build the preview site 7 | * - upload-preview: upload the preview site to S3 8 | * - remove-preview: removes the preview site from S3 9 | * 10 | * Note: please set the environment variable S3_PREVIEW_DOMAIN to the domain 11 | * used to host your S3 preview. 12 | */ 13 | 14 | 'use strict'; 15 | 16 | var gulp = require('gulp'), 17 | runSequence = require('gulp4-run-sequence'), 18 | childProcess = require('child_process'), 19 | webpack = require('webpack'), 20 | webpackStream = require('webpack-stream'), 21 | gitUtils = require('../utils/git-utils'), 22 | config = require('../config').documentation, 23 | webpackConfig = require('../../webpack.config.js'), 24 | previewConfigFile = '_tmp_preview_config.yml', 25 | previewDomain = process.env.S3_PREVIEW_DOMAIN; 26 | 27 | gulp.task('preview', function(callback) { 28 | runSequence( 29 | 'clean', 30 | 'doc-build', 31 | 'jekyll-build-preview', 32 | 'preview-webpack', 33 | 'upload-preview', 34 | 'show-preview', 35 | callback 36 | ); 37 | }); 38 | 39 | gulp.task('jekyll-build-preview', function() { 40 | var branch = gitUtils.currentBranch(), 41 | previewBaseUrl = '/' + branch + '/'; 42 | // Create a temporary Jekyll configuration file which specifies the base URL for the preview site 43 | childProcess.execSync( 44 | 'echo \'baseurl: ' + previewBaseUrl + '\' > ' + previewConfigFile 45 | ); 46 | 47 | // Generate the preview version of the site 48 | console.log('Generating preview for branch ' + branch); 49 | childProcess.execSync( 50 | 'jekyll build --config _config.yml,' + previewConfigFile + ' --destination ' + config.previewTargetDir 51 | ); 52 | 53 | // Remove the configuration file since it is no longer needed 54 | childProcess.execSync('rm ' + previewConfigFile); 55 | }); 56 | 57 | gulp.task('preview-webpack', function() { 58 | var outputPath = config.previewTargetDir + '/public/', 59 | branch = gitUtils.currentBranch(); 60 | process.env.SITE_ROOT = '/' + branch + '/'; 61 | return gulp.src('') 62 | .pipe(webpackStream(webpackConfig, webpack)) 63 | .pipe(gulp.dest(outputPath)); 64 | }); 65 | 66 | gulp.task('upload-preview', function() { 67 | var branch = gitUtils.currentBranch(); 68 | if (previewDomain) { 69 | childProcess.execSync( 70 | 'aws s3 sync ' + config.previewTargetDir + ' s3://' + previewDomain + '/' + branch 71 | ); 72 | console.log('Preview site ready at http://' + previewDomain + '/' + branch); 73 | } else { 74 | console.log( 75 | 'No preview domain specified. Please export environment variable S3_PREVIEW_DOMAIN and try again.' 76 | ); 77 | } 78 | }); 79 | 80 | gulp.task('remove-preview', function() { 81 | var branch = gitUtils.currentBranch(); 82 | childProcess.execSync( 83 | 'aws s3 rm --recursive s3://' + previewDomain + '/' + branch 84 | ); 85 | console.log('Removed preview for branch ' + branch); 86 | }); 87 | 88 | gulp.task('show-preview', function() { 89 | var branch = gitUtils.currentBranch(); 90 | childProcess.execSync( 91 | 'open http://' + previewDomain + '/' + branch 92 | ); 93 | }); 94 | -------------------------------------------------------------------------------- /gulp/tasks/test-debug.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | karma = require('karma'), 5 | path = require('path'), 6 | configFile = 'test/karma.debug.conf.js'; 7 | 8 | gulp.task('test-debug', function(callback) { 9 | new karma.Server({ 10 | configFile: path.resolve(configFile) 11 | }, callback).start(); 12 | }); 13 | -------------------------------------------------------------------------------- /gulp/tasks/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'), 4 | karma = require('karma'), 5 | path = require('path'), 6 | configFile; 7 | 8 | 9 | gulp.task('test', function(callback) { 10 | if (process.argv.indexOf('--ci') !== -1) { 11 | configFile = 'test/karma.ci.conf.js'; 12 | } else { 13 | configFile = 'test/karma.conf.js'; 14 | } 15 | new karma.Server({ 16 | configFile: path.resolve(configFile) 17 | }, callback).start(); 18 | }); 19 | 20 | exports.test = 'test'; 21 | -------------------------------------------------------------------------------- /gulp/utils/generate-doc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var jsdox = require('jsdox'), 4 | path = require('path'), 5 | through = require('through2'), 6 | GulpUtil = require('gulp-util'), 7 | jsdocParser = require('jsdoc3-parser'), 8 | PLUGIN_NAME = 'generate-doc', 9 | analyze = jsdox.analyze, 10 | generateMD = jsdox.generateMD, 11 | jsdoxTemplatesDir = 'node_modules/jsdox/templates'; 12 | 13 | module.exports = function(viewClass) { 14 | return through.obj(function(file, enc, next) { 15 | var self = this; 16 | if (file.isStream()) { 17 | jsdocParser(file.history, function(err, result) { 18 | var frontMatter, data, markdown, title, relativePath, requirePath, gitHubPath, 19 | fileToPush = file; 20 | if (err) { 21 | console.error('Error generating docs for file', file, err); 22 | next(err); 23 | } else { 24 | // Generate the front matter 25 | relativePath = path.relative(path.resolve('src'), file.path); 26 | requirePath = 'edx-ui-toolkit/' + relativePath.slice(0, -3); 27 | gitHubPath = 'blob/master/src/' + relativePath; 28 | // title = path.relative(path.resolve('src/js'), file.path).slice(0, -3); 29 | title = path.basename(file.path, '.js'); 30 | frontMatter = '---\n' + 31 | 'title: ' + title + '\n' + 32 | 'requirePath: ' + requirePath + '\n' + 33 | 'githubPath: ' + gitHubPath + '\n' + 34 | 'viewClass: ' + viewClass + '\n' + 35 | '---\n\n'; 36 | 37 | // Generate the markdown 38 | data = analyze(result, {}); 39 | markdown = generateMD(data, jsdoxTemplatesDir); 40 | 41 | // set the result to the front matter followed by the markdown 42 | fileToPush.contents = new Buffer(frontMatter + markdown); 43 | } 44 | self.push(fileToPush); 45 | next(); 46 | }); 47 | } else { 48 | this.emit('error', new GulpUtil.PluginError(PLUGIN_NAME, 'Non-streams not supported!')); 49 | next(); 50 | } 51 | }); 52 | }; 53 | -------------------------------------------------------------------------------- /gulp/utils/git-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var childProcess = require('child_process'), 4 | gitBranchCommand = 'git rev-parse --abbrev-ref HEAD'; 5 | 6 | module.exports = { 7 | /** 8 | * Returns the current Git branch for the current directory. 9 | */ 10 | currentBranch: function() { 11 | var branch = childProcess.execSync(gitBranchCommand); 12 | return branch.toString().trim(); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // gulpfile.js 2 | 3 | // Rather than manage one giant configuration file responsible 4 | // for creating multiple tasks, each task has been broken out into 5 | // its own file in gulp/tasks. Any files in that directory get 6 | // automatically required below. 7 | 8 | // To add a new task, simply add a new task file that directory. 9 | // gulp/tasks/default.js specifies the default set of tasks to run 10 | // when you run `gulp`. 11 | 12 | var defaulTasks = require('./gulp/tasks/default'); 13 | 14 | exports.default = defaulTasks.default; 15 | -------------------------------------------------------------------------------- /openedx.yaml: -------------------------------------------------------------------------------- 1 | # This file describes this Open edX repo, as described in OEP-2: 2 | # http://open-edx-proposals.readthedocs.io/en/latest/oeps/oep-0002.html#specification 3 | 4 | nick: uitk 5 | oeps: {} 6 | owner: edx/fedX-team 7 | track-pulls: true 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edx-ui-toolkit", 3 | "version": "1.0.0-semantically-released", 4 | "description": "A JavaScript toolkit for building edX user interfaces", 5 | "license": "Apache-2.0", 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/openedx/edx-ui-toolkit" 9 | }, 10 | "keywords": [ 11 | "edx", 12 | "open edx", 13 | "javascript" 14 | ], 15 | "homepage": "https://github.com/openedx/edx-ui-toolkit", 16 | "scripts": { 17 | "test": "gulp test", 18 | "lint": "eslint ." 19 | }, 20 | "dependencies": { 21 | "backbone": "1.6.1", 22 | "backbone.paginator": "2.0.8", 23 | "gulp-shell": "0.8.0", 24 | "jquery": "~3.7.0", 25 | "mini-css-extract-plugin": "^2.7.2", 26 | "moment": "2.30.1", 27 | "moment-timezone": "0.6.0", 28 | "requirejs": "2.1.22", 29 | "requirejs-text": "2.0.16", 30 | "sass": "^1.58.3", 31 | "sinon": "1.17.7", 32 | "underscore": "~1.8.3", 33 | "urijs": "1.19.11" 34 | }, 35 | "devDependencies": { 36 | "@edx/eslint-config": "^4.0.0", 37 | "bi-app-sass": "1.1.0", 38 | "bourbon": "4.3.4", 39 | "browser-sync": "3.0.4", 40 | "css-loader": "^6.0.0", 41 | "del": "2.2.2", 42 | "eslint": "^8.35.0", 43 | "eslint-config-edx": "4.0.4", 44 | "gulp": "^4.0.2", 45 | "gulp-gh-pages": "0.5.4", 46 | "gulp-rename": "2.0.0", 47 | "gulp-util": "3.0.8", 48 | "gulp4-run-sequence": "^1.0.1", 49 | "jasmine": "5.7.1", 50 | "jasmine-core": "5.7.1", 51 | "jsdoc3-parser": "3.0.0", 52 | "jsdox": "0.4.10", 53 | "karma": "6.4.4", 54 | "karma-chrome-launcher": "^3.1.1", 55 | "karma-coverage": "2.2.1", 56 | "karma-firefox-launcher": "2.1.3", 57 | "karma-jasmine": "5.1.0", 58 | "karma-jasmine-html-reporter": "2.1.0", 59 | "karma-jasmine-jquery-2": "^0.1.1", 60 | "karma-phantomjs-launcher": "1.0.4", 61 | "karma-requirejs": "1.1.0", 62 | "karma-sinon": "1.0.5", 63 | "karma-spec-reporter": "0.0.36", 64 | "puppeteer": "^14.1.2", 65 | "require-dir": "latest", 66 | "requirejs-plugins": "1.0.2", 67 | "sass-loader": "^13.2.0", 68 | "susy": "2.2.14", 69 | "style-loader": "3.3.4", 70 | "through2": "2.0.5", 71 | "webpack": "^5.75.0", 72 | "webpack-stream": "7.0.0" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | "schedule:weekly", 5 | ":automergeLinters", 6 | ":automergeMinor", 7 | ":automergeTesters", 8 | ":enableVulnerabilityAlerts", 9 | ":rebaseStalePrs", 10 | ":semanticCommits", 11 | ":updateNotScheduled" 12 | ], 13 | "packageRules": [ 14 | { 15 | "matchDepTypes": [ 16 | "devDependencies" 17 | ], 18 | "matchUpdateTypes": [ 19 | "lockFileMaintenance", 20 | "minor", 21 | "patch", 22 | "pin" 23 | ], 24 | "automerge": true 25 | } 26 | ], 27 | "timezone": "America/New_York" 28 | } 29 | -------------------------------------------------------------------------------- /src/js/breadcrumbs/breadcrumbs-model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Backbone model that works with the BreadcrumbsView to provide breadcrumb navigation 3 | * 4 | * Here's what initializing a BreadcrumbsModel looks like: 5 | * 6 | *~~~ javascript 7 | * var model = new BreadcrumbsModel({ 8 | * breadcrumbs: [ 9 | * { 10 | * url: '/', 11 | * title: 'Item List' 12 | * }, 13 | * { 14 | * url: '/details/1', 15 | * title: 'Item Details' 16 | * } 17 | * ], 18 | * label: 'Demo Page' 19 | * }); 20 | *~~~ 21 | * @module BreadcrumbsModel 22 | */ 23 | (function(define) { 24 | 'use strict'; 25 | 26 | define(['backbone'], function(Backbone) { 27 | var BreadcrumbsModel = Backbone.Model.extend({ 28 | defaults: { 29 | breadcrumbs: null, 30 | label: '' 31 | } 32 | }); 33 | 34 | return BreadcrumbsModel; 35 | }); 36 | }).call( 37 | this, 38 | // Use the default 'define' function if available, else use 'RequireJS.define' 39 | typeof define === 'function' && define.amd ? define : RequireJS.define 40 | ); 41 | -------------------------------------------------------------------------------- /src/js/breadcrumbs/breadcrumbs-view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Backbone view that renders breadcrumbs-type tiered navigation. 3 | * 4 | * Initialize the view by passing in the following attributes: 5 | * 6 | *~~~ javascript 7 | * var view = new BreadcrumbsView({ 8 | * el: $('selector for element that will contain breadcrumbs'), 9 | * model: new BreadcrumbsModel({ 10 | * breadcrumbs: [{url: '/', title: 'Overview'}] 11 | * }), 12 | * events: { 13 | * 'click nav.breadcrumbs a.nav-item': function (event) { 14 | * event.preventDefault(); 15 | * window.location = $(event.currentTarget).attr('href'); 16 | * } 17 | * } 18 | * }); 19 | *~~~ 20 | * @module BreadcrumbsView 21 | */ 22 | (function(define) { 23 | 'use strict'; 24 | 25 | define(['backbone', 'edx-ui-toolkit/js/utils/html-utils', 'text!./breadcrumbs.underscore'], 26 | function(Backbone, HtmlUtils, breadcrumbsTemplate) { 27 | var BreadcrumbsView = Backbone.View.extend({ 28 | initialize: function() { 29 | this.template = HtmlUtils.template(breadcrumbsTemplate); 30 | this.listenTo(this.model, 'change', this.render); 31 | this.render(); 32 | }, 33 | 34 | render: function() { 35 | var json = this.model.attributes; 36 | HtmlUtils.setHtml(this.$el, this.template(json)); 37 | return this; 38 | } 39 | }); 40 | 41 | return BreadcrumbsView; 42 | }); 43 | }).call( 44 | this, 45 | // Use the default 'define' function if available, else use 'RequireJS.define' 46 | typeof define === 'function' && define.amd ? define : RequireJS.define 47 | ); 48 | -------------------------------------------------------------------------------- /src/js/breadcrumbs/breadcrumbs.underscore: -------------------------------------------------------------------------------- 1 | <% if (breadcrumbs !== null && breadcrumbs.length > 0) { %> 2 | 11 | <% } %> 12 | -------------------------------------------------------------------------------- /src/js/breadcrumbs/specs/breadcrumbs-spec.js: -------------------------------------------------------------------------------- 1 | (function(define) { 2 | 'use strict'; 3 | 4 | define([ 5 | 'jquery', 6 | '../../utils/html-utils.js', 7 | '../../utils/spec-helpers/spec-helpers.js', 8 | '../breadcrumbs-view.js', 9 | '../breadcrumbs-model.js' 10 | ], 11 | function($, HtmlUtils, SpecHelpers, BreadcrumbsView, BreadcrumbsModel) { 12 | describe('BreadcrumbsView', function() { 13 | var model, view; 14 | 15 | beforeEach(function() { 16 | model = new BreadcrumbsModel(); 17 | view = new BreadcrumbsView({ 18 | model: model 19 | }); 20 | }); 21 | 22 | it('does not show breadcrumbs by default', function() { 23 | expect(view.$el.html()).not.toContain('