├── app ├── .gitkeep ├── services │ └── body-class.js └── helpers │ └── set-body-class.js ├── addon ├── .gitkeep ├── helpers │ └── set-body-class.js └── services │ └── body-class.js ├── vendor └── .gitkeep ├── tests ├── dummy │ ├── app │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── models │ │ │ └── .gitkeep │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── templates │ │ │ ├── components │ │ │ │ └── .gitkeep │ │ │ └── application.hbs │ │ ├── router.js │ │ ├── styles │ │ │ └── app.css │ │ ├── app.js │ │ └── index.html │ ├── public │ │ ├── robots.txt │ │ └── crossdomain.xml │ └── config │ │ ├── targets.js │ │ ├── optional-features.json │ │ └── environment.js ├── test-helper.js ├── fastboot │ └── set-body-class-test.js ├── acceptance │ ├── test-bleed-test.js │ └── index-test.js └── index.html ├── .watchmanconfig ├── index.js ├── config ├── environment.js └── ember-try.js ├── .npmignore ├── .prettierrc.js ├── .template-lintrc.js ├── .ember-cli ├── .eslintignore ├── .release-it.js ├── renovate.json ├── .gitignore ├── .editorconfig ├── ember-cli-build.js ├── testem.js ├── .github └── workflows │ ├── release.yml │ └── ci.yml ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── .eslintrc.js ├── package.json ├── RELEASE.md └── CHANGELOG.md /app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/services/body-class.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-set-body-class/services/body-class'; 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: 'ember-set-body-class', 5 | }; 6 | -------------------------------------------------------------------------------- /app/helpers/set-body-class.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-set-body-class/helpers/set-body-class'; 2 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | browsers: ['last 1 Chrome versions', 'last 1 year'], 5 | }; 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /config/ember-try.js 2 | /dist 3 | /tests 4 | /tmp 5 | .* 6 | *.tgz 7 | ember-cli-build.js 8 | RELEASE.md 9 | renovate.json 10 | testem.js 11 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | printWidth: 100, 5 | semi: true, 6 | arrowParens: 'avoid', 7 | singleQuote: true, 8 | trailingComma: 'es5', 9 | }; 10 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'octane', 5 | rules: { 6 | 'no-action': 'off', 7 | 'no-curly-component-invocation': 'off', 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | start(); 8 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from './config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default class ApplicationController extends Controller { 4 | redText = true; 5 | blueBackground = false; 6 | textSize = 'small-text'; 7 | textSizes = ['small-text', 'medium-text', 'large-text']; 8 | } 9 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | body.red-text { 2 | color: red; 3 | } 4 | 5 | body.blue-background { 6 | background-color: #6666ff; 7 | } 8 | 9 | body.small-text { 10 | font-size: medium; 11 | } 12 | body.medium-text { 13 | font-size: large; 14 | } 15 | body.large-text { 16 | font-size: xx-large; 17 | } 18 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /.release-it.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'release-it-lerna-changelog': { 4 | infile: 'CHANGELOG.md', 5 | }, 6 | }, 7 | git: { 8 | commitMessage: 'v${version}', 9 | tagAnnotation: 'v${version}', 10 | }, 11 | github: { 12 | release: true, 13 | releaseName: 'v${version}', 14 | tokenRef: 'GITHUB_AUTH', 15 | }, 16 | npm: { 17 | publish: false, 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:js-lib", 4 | ":automergePatch", 5 | ":dependencyDashboard", 6 | ":maintainLockFilesWeekly", 7 | ":semanticCommitsDisabled" 8 | ], 9 | 10 | "postUpdateOptions": ["yarnDedupeHighest"], 11 | 12 | "packageRules": [ 13 | { 14 | "matchCurrentVersion": ">= 1.0.0", 15 | "updateTypes": ["minor"], 16 | "automerge": true 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/* 15 | /libpeerconnection.log 16 | npm-debug.log* 17 | yarn-error.log 18 | testem.log 19 | 20 | # ember-try 21 | .node_modules.ember-try/ 22 | bower.json.ember-try 23 | package.json.ember-try 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /tests/fastboot/set-body-class-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setup, visit } from 'ember-cli-fastboot-testing/test-support'; 3 | 4 | module('Fastboot | set-body-class', function (hooks) { 5 | setup(hooks); 6 | 7 | test('it works', async function (assert) { 8 | let { htmlDocument } = await visit('/'); 9 | assert.dom(htmlDocument.body).hasClass('red-text'); 10 | assert.dom(htmlDocument.body).hasClass('small-text'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | let app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | return app.toTree(); 18 | }; 19 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_args: { 9 | Chrome: { 10 | mode: 'ci', 11 | args: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.TRAVIS ? '--no-sandbox' : null, 14 | '--disable-gpu', 15 | '--headless', 16 | '--remote-debugging-port=9222', 17 | '--window-size=1440,900', 18 | ].filter(Boolean), 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /addon/helpers/set-body-class.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | import { guidFor } from '@ember/object/internals'; 3 | import { inject as service } from '@ember/service'; 4 | 5 | export default class SetBodyClassHelper extends Helper { 6 | @service bodyClass; 7 | 8 | id = guidFor(this); 9 | 10 | compute([_classNames]) { 11 | let classNames = _classNames ? _classNames.split(/\s+/) : []; 12 | this.bodyClass.register(this.id, classNames); 13 | } 14 | 15 | willDestroy() { 16 | super.willDestroy(...arguments); 17 | this.bodyClass.deregister(this.id); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | release: 10 | name: release 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v2 16 | with: 17 | node-version: 10 18 | registry-url: 'https://registry.npmjs.org' 19 | 20 | - run: yarn install --frozen-lockfile 21 | 22 | - name: auto-dist-tag 23 | run: npx auto-dist-tag@1 --write 24 | 25 | - run: npm publish 26 | env: 27 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 28 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/acceptance/test-bleed-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Acceptance | CSS class bleeding between tests', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('test 1 sets a class', async function (assert) { 10 | assert.dom(document.body).hasNoClass('foo'); 11 | await render(hbs`{{set-body-class "foo"}}`); 12 | assert.dom(document.body).hasClass('foo'); 13 | }); 14 | 15 | test('test 2 ensures that the class is not set anymore', async function (assert) { 16 | assert.dom(document.body).hasNoClass('foo'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone git@github.com:ef4/ember-set-body-class.git` 6 | * `cd ember-set-body-class` 7 | * `yarn install` 8 | 9 | ## Linting 10 | 11 | * `yarn lint:hbs` 12 | * `yarn lint:js` 13 | * `yarn lint:js --fix` 14 | 15 | ## Running tests 16 | 17 | * `ember test` – Runs the test suite on the current Ember version 18 | * `ember test --server` – Runs the test suite in "watch mode" 19 | * `ember try:each` – Runs the test suite against multiple Ember versions 20 | 21 | ## Running the dummy application 22 | 23 | * `ember serve` 24 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 25 | 26 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 27 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.redText}} 2 | {{set-body-class "red-text"}} 3 | {{/if}} 4 | 5 | {{#if this.blueBackground}} 6 | {{set-body-class "blue-background"}} 7 | {{/if}} 8 | 9 | {{set-body-class this.textSize}} 10 | 11 | 12 | 13 |
14 | 15 | Text Size: 16 | 21 | 22 |
23 | 24 | Any class name: 25 | {{input value=this.dynamicClassName name="dynamicClassName"}} 26 | {{set-body-class this.dynamicClassName}} 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ember-set-body-class 2 | ============================================================================== 3 | 4 | This addon lets you set classes on the body like: 5 | 6 | ```hbs 7 | {{set-body-class "hello"}} 8 | ``` 9 | 10 | Whenever the above template is rendered, the `` tag will have class 11 | `hello` added to it. When it's destroyed, the class is removed. 12 | 13 | 14 | Compatibility 15 | ------------------------------------------------------------------------------ 16 | 17 | * Ember.js v3.10 or above 18 | * Ember CLI v2.13 or above 19 | * Node.js v10 or above 20 | 21 | This works in both browser and Fastboot. Fastboot support requires at least 22 | Fastboot 1.1.1 (which incorporated the work done in https://github.com/ember-fastboot/fastboot/pull/171). 23 | 24 | 25 | Installation 26 | ------------------------------------------------------------------------------ 27 | 28 | ``` 29 | ember install ember-set-body-class 30 | ``` 31 | 32 | 33 | Contributing 34 | ------------------------------------------------------------------------------ 35 | 36 | See the [Contributing](CONTRIBUTING.md) guide for details. 37 | 38 | 39 | License 40 | ------------------------------------------------------------------------------ 41 | 42 | This project is licensed under the [MIT License](LICENSE.md). 43 | -------------------------------------------------------------------------------- /tests/acceptance/index-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupApplicationTest } from 'ember-qunit'; 3 | import { click, fillIn, visit } from '@ember/test-helpers'; 4 | 5 | module('Acceptance | index', function (hooks) { 6 | setupApplicationTest(hooks); 7 | 8 | test('updates body class', async function (assert) { 9 | await visit('/'); 10 | assert.dom(document.body).hasClass('red-text').hasClass('unrelated-class'); 11 | 12 | // ensure that `{{set-body-class}}` does not render anything to the DOM 13 | let selectBox = this.element.querySelector('[data-test-select]'); 14 | let app = selectBox.parentElement; 15 | assert.equal(app.firstElementChild.tagName, 'LABEL'); 16 | 17 | await click('input[name=blueBackground]'); 18 | assert 19 | .dom(document.body) 20 | .hasClass('red-text') 21 | .hasClass('blue-background') 22 | .hasClass('unrelated-class'); 23 | 24 | await click('input[name=redText]'); 25 | assert 26 | .dom(document.body) 27 | .doesNotHaveClass('red-text') 28 | .hasClass('blue-background') 29 | .hasClass('unrelated-class'); 30 | 31 | await fillIn('input[name=dynamicClassName]', 'blue-background'); 32 | await click('input[name=blueBackground]'); 33 | assert.dom(document.body).hasClass('blue-background').hasClass('unrelated-class'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false, 17 | }, 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | }, 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: ['eslint:recommended', 'plugin:ember/recommended', 'plugin:prettier/recommended'], 15 | env: { 16 | browser: true, 17 | }, 18 | rules: { 19 | // these rules disabled for now, but should be fixed eventually 20 | 'ember/no-arrow-function-computed-properties': 'off', 21 | 'ember/no-get': 'off', 22 | 'ember/no-observers': 'off', 23 | }, 24 | overrides: [ 25 | // test files 26 | { 27 | files: ['tests/**'], 28 | excludedFiles: ['tests/dummy/**'], 29 | rules: { 30 | 'ember/avoid-leaking-state-in-ember-objects': 'off', 31 | }, 32 | }, 33 | 34 | // node files 35 | { 36 | files: [ 37 | '.eslintrc.js', 38 | '.prettierrc.js', 39 | '.release-it.js', 40 | '.template-lintrc.js', 41 | 'ember-cli-build.js', 42 | 'index.js', 43 | 'testem.js', 44 | 'blueprints/*/index.js', 45 | 'config/**/*.js', 46 | 'tests/dummy/config/**/*.js', 47 | ], 48 | excludedFiles: ['addon/**', 'addon-test-support/**', 'app/**', 'tests/dummy/app/**'], 49 | parserOptions: { 50 | sourceType: 'script', 51 | }, 52 | env: { 53 | browser: false, 54 | node: true, 55 | }, 56 | plugins: ['node'], 57 | extends: ['plugin:node/recommended'], 58 | }, 59 | ], 60 | }; 61 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - 'v*' 8 | pull_request: 9 | 10 | jobs: 11 | test: 12 | name: Tests 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: volta-cli/action@v1 18 | with: 19 | node-version: 10.x 20 | yarn-version: 1.22.5 21 | 22 | - run: yarn install --frozen-lockfile 23 | - run: yarn lint:js 24 | - run: yarn lint:hbs 25 | - run: yarn ember test 26 | 27 | floating-dependencies: 28 | name: Floating Dependencies 29 | runs-on: ubuntu-latest 30 | 31 | steps: 32 | - uses: actions/checkout@v2 33 | - uses: volta-cli/action@v1 34 | with: 35 | node-version: 10.x 36 | yarn-version: 1.22.5 37 | 38 | - run: yarn install --no-lockfile 39 | - run: yarn ember test 40 | 41 | try-scenarios: 42 | name: "ember-try: ${{ matrix.ember-try-scenario }}" 43 | runs-on: ubuntu-latest 44 | needs: test 45 | 46 | strategy: 47 | fail-fast: true 48 | matrix: 49 | ember-try-scenario: 50 | - ember-3.10 51 | - ember-lts-3.12 52 | - ember-lts-3.16 53 | - ember-lts-3.20 54 | - ember-release 55 | - ember-beta 56 | - ember-canary 57 | - ember-default-with-jquery 58 | - ember-classic 59 | 60 | steps: 61 | - uses: actions/checkout@v2 62 | - uses: volta-cli/action@v1 63 | with: 64 | node-version: 12.x 65 | yarn-version: 1.22.5 66 | 67 | - run: yarn install --frozen-lockfile 68 | 69 | - name: test 70 | run: yarn ember try:one ${{ matrix.ember-try-scenario }} --skip-cleanup 71 | -------------------------------------------------------------------------------- /addon/services/body-class.js: -------------------------------------------------------------------------------- 1 | import { getOwner } from '@ember/application'; 2 | import Service from '@ember/service'; 3 | import { A } from '@ember/array'; 4 | import { once, cancel } from '@ember/runloop'; 5 | 6 | export default class BodyClassService extends Service { 7 | _dom = getOwner(this).lookup('service:-document'); 8 | _fastboot = getOwner(this).lookup('service:fastboot'); 9 | registrations = new Map(); 10 | 11 | register(id, classNames) { 12 | this.registrations.set(id, classNames); 13 | this.scheduleUpdate(); 14 | } 15 | 16 | deregister(id) { 17 | this.registrations.delete(id); 18 | this.scheduleUpdate(); 19 | } 20 | 21 | get names() { 22 | let allNames = new Set(); 23 | for (let classNames of this.registrations.values()) { 24 | for (let className of classNames) { 25 | allNames.add(className); 26 | } 27 | } 28 | return [...allNames]; 29 | } 30 | 31 | scheduleUpdate() { 32 | this.scheduledRun = once(this, this.updateBodyClass); 33 | } 34 | 35 | updateBodyClass() { 36 | if (!this._dom) { 37 | return; 38 | } 39 | 40 | let registeredClassNames = this.names; 41 | 42 | let body = this._dom.body; 43 | let attr = body.getAttribute('class'); 44 | let classList = A(attr ? attr.split(/\s+/) : []); 45 | 46 | classList.removeObjects(this._previousNames || []); 47 | classList.addObjects(registeredClassNames); 48 | 49 | this._previousNames = registeredClassNames; 50 | 51 | body.setAttribute('class', classList.join(' ')); 52 | } 53 | 54 | willDestroy() { 55 | if (this._fastboot && this._fastboot.isFastBoot) { 56 | // prevent FastBoot from removing the CSS classes 57 | // again before the response is sent out 58 | cancel(this.scheduledRun); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-set-body-class", 3 | "version": "1.0.2", 4 | "description": "Fastboot-compatible, template-driven helper for setting classes.", 5 | "keywords": [ 6 | "ember-addon", 7 | "body", 8 | "class", 9 | "fastboot" 10 | ], 11 | "license": "MIT", 12 | "author": "Edward Faulkner ", 13 | "directories": { 14 | "doc": "doc", 15 | "test": "tests" 16 | }, 17 | "repository": "https://github.com/ef4/ember-set-body-class", 18 | "scripts": { 19 | "build": "ember build", 20 | "lint:hbs": "ember-template-lint .", 21 | "lint:js": "eslint .", 22 | "release": "release-it", 23 | "start": "ember server", 24 | "test": "ember try:each" 25 | }, 26 | "dependencies": { 27 | "ember-cli-babel": "^7.22.1" 28 | }, 29 | "devDependencies": { 30 | "@ember/optional-features": "2.0.0", 31 | "babel-eslint": "10.1.0", 32 | "ember-cli": "3.27.0", 33 | "ember-cli-dependency-checker": "3.2.0", 34 | "ember-cli-fastboot": "2.2.3", 35 | "ember-cli-fastboot-testing": "0.5.0", 36 | "ember-cli-htmlbars": "5.7.1", 37 | "ember-cli-htmlbars-inline-precompile": "3.0.2", 38 | "ember-cli-inject-live-reload": "2.1.0", 39 | "ember-disable-prototype-extensions": "1.1.3", 40 | "ember-load-initializers": "2.1.2", 41 | "ember-qunit": "4.6.0", 42 | "ember-resolver": "8.0.2", 43 | "ember-sinon": "5.0.0", 44 | "ember-source": "3.27.5", 45 | "ember-source-channel-url": "3.0.0", 46 | "ember-template-lint": "2.21.0", 47 | "ember-try": "1.4.0", 48 | "eslint": "7.29.0", 49 | "eslint-config-prettier": "8.3.0", 50 | "eslint-plugin-ember": "10.5.1", 51 | "eslint-plugin-node": "11.1.0", 52 | "eslint-plugin-prettier": "3.4.0", 53 | "loader.js": "4.7.0", 54 | "prettier": "2.3.2", 55 | "qunit-dom": "1.6.0", 56 | "release-it": "14.10.0", 57 | "release-it-lerna-changelog": "3.1.0" 58 | }, 59 | "engines": { 60 | "node": "10.* || 12.* || >= 14.*" 61 | }, 62 | "ember-addon": { 63 | "configPath": "tests/dummy/config" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | Releases are mostly automated using 4 | [release-it](https://github.com/release-it/release-it/) and 5 | [lerna-changelog](https://github.com/lerna/lerna-changelog/). 6 | 7 | 8 | ## Preparation 9 | 10 | Since the majority of the actual release process is automated, the primary 11 | remaining task prior to releasing is confirming that all pull requests that 12 | have been merged since the last release have been labeled with the appropriate 13 | `lerna-changelog` labels and the titles have been updated to ensure they 14 | represent something that would make sense to our users. Some great information 15 | on why this is important can be found at 16 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 17 | guiding principle here is that changelogs are for humans, not machines. 18 | 19 | When reviewing merged PR's the labels to be used are: 20 | 21 | * breaking - Used when the PR is considered a breaking change. 22 | * enhancement - Used when the PR adds a new feature or enhancement. 23 | * bug - Used when the PR fixes a bug included in a previous release. 24 | * documentation - Used when the PR adds or updates documentation. 25 | * internal - Used for internal changes that still require a mention in the 26 | changelog/release notes. 27 | 28 | 29 | ## Release 30 | 31 | Once the prep work is completed, the actual release is straight forward: 32 | 33 | * First, ensure that you have an environment variable with your GitHub token 34 | setup as `GITHUB_AUTH`. This token will be used for generating your changelog 35 | (unauthenticated requests to the GitHub API are heavily throttled) and for 36 | creating the GitHub release. Only "repo" access is needed; no "admin" 37 | or other scopes are required. 38 | 39 | * Next, ensure that you have installed your projects dependencies: 40 | 41 | ``` 42 | yarn install 43 | ``` 44 | 45 | * And last (but not least 😁) do your release: 46 | 47 | ``` 48 | yarn release 49 | ``` 50 | 51 | [release-it](https://github.com/release-it/release-it/) manages the actual 52 | release process. It will prompt you to to choose the version number after which 53 | you will have the chance to hand tweak the changelog to be used (for the 54 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 55 | pushing the tag and commits, etc. Finally GitHub Actions will build the commit 56 | and push the release to npm. 57 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | 5 | module.exports = async function () { 6 | return { 7 | useYarn: true, 8 | scenarios: [ 9 | { 10 | name: 'ember-3.10', 11 | npm: { 12 | devDependencies: { 13 | 'ember-source': '~3.10.0', 14 | }, 15 | }, 16 | }, 17 | { 18 | name: 'ember-lts-3.12', 19 | npm: { 20 | devDependencies: { 21 | 'ember-source': '~3.12.0', 22 | }, 23 | }, 24 | }, 25 | { 26 | name: 'ember-lts-3.16', 27 | npm: { 28 | devDependencies: { 29 | 'ember-source': '~3.16.0', 30 | }, 31 | }, 32 | }, 33 | { 34 | name: 'ember-lts-3.20', 35 | npm: { 36 | devDependencies: { 37 | 'ember-source': '~3.20.0', 38 | }, 39 | }, 40 | }, 41 | { 42 | name: 'ember-release', 43 | npm: { 44 | devDependencies: { 45 | 'ember-source': await getChannelURL('release'), 46 | }, 47 | }, 48 | }, 49 | { 50 | name: 'ember-beta', 51 | npm: { 52 | devDependencies: { 53 | 'ember-source': await getChannelURL('beta'), 54 | }, 55 | }, 56 | }, 57 | { 58 | name: 'ember-canary', 59 | npm: { 60 | devDependencies: { 61 | 'ember-source': await getChannelURL('canary'), 62 | }, 63 | }, 64 | }, 65 | { 66 | name: 'ember-default-with-jquery', 67 | env: { 68 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 69 | 'jquery-integration': true, 70 | }), 71 | }, 72 | npm: { 73 | devDependencies: { 74 | '@ember/jquery': '^1.1.0', 75 | }, 76 | }, 77 | }, 78 | { 79 | name: 'ember-classic', 80 | env: { 81 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 82 | 'application-template-wrapper': true, 83 | 'default-async-observers': false, 84 | 'template-only-glimmer-components': false, 85 | }), 86 | }, 87 | npm: { 88 | ember: { 89 | edition: 'classic', 90 | }, 91 | }, 92 | }, 93 | ], 94 | }; 95 | }; 96 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.0.2 (2020-12-02) 2 | 3 | #### :bug: Bug Fix 4 | * [#92](https://github.com/ef4/ember-set-body-class/pull/92) services/body-class: Cancel the scheduled update only on fastboot ([@Turbo87](https://github.com/Turbo87)) 5 | 6 | #### :house: Internal 7 | * [#82](https://github.com/ef4/ember-set-body-class/pull/82) Adjust browser targets to transpile optional chaining away ([@Turbo87](https://github.com/Turbo87)) 8 | 9 | #### Committers: 1 10 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 11 | 12 | ## v1.0.1 (2020-10-16) 13 | 14 | #### :memo: Documentation 15 | * [#64](https://github.com/ef4/ember-set-body-class/pull/64) Update README and add basic CONTRIBUTING guide ([@Turbo87](https://github.com/Turbo87)) 16 | 17 | #### :house: Internal 18 | * [#70](https://github.com/ef4/ember-set-body-class/pull/70) Convert dummy app objects to native classes ([@Turbo87](https://github.com/Turbo87)) 19 | * [#71](https://github.com/ef4/ember-set-body-class/pull/71) template-lint: Fix `no-implicit-this` warnings ([@Turbo87](https://github.com/Turbo87)) 20 | * [#68](https://github.com/ef4/ember-set-body-class/pull/68) Add `ember-template-lint` ([@Turbo87](https://github.com/Turbo87)) 21 | * [#67](https://github.com/ef4/ember-set-body-class/pull/67) Remove obsolete `eslint-env` directives ([@Turbo87](https://github.com/Turbo87)) 22 | * [#65](https://github.com/ef4/ember-set-body-class/pull/65) Add `@ember/optional-features` and CI scenarios without it ([@Turbo87](https://github.com/Turbo87)) 23 | * [#63](https://github.com/ef4/ember-set-body-class/pull/63) Update `.npmignore` file ([@Turbo87](https://github.com/Turbo87)) 24 | 25 | #### Committers: 1 26 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 27 | 28 | ## v1.0.0 (2020-10-16) 29 | 30 | #### :boom: Breaking Change 31 | * [#54](https://github.com/ef4/ember-set-body-class/pull/54) Replace `set-body-class` component with helper ([@Turbo87](https://github.com/Turbo87)) 32 | * [#55](https://github.com/ef4/ember-set-body-class/pull/55) Drop support for Ember.js v3.8 and v3.9 ([@Turbo87](https://github.com/Turbo87)) 33 | * [#24](https://github.com/ef4/ember-set-body-class/pull/24) Drop support for Ember.js v3.7.x and below ([@Turbo87](https://github.com/Turbo87)) 34 | * [#13](https://github.com/ef4/ember-set-body-class/pull/13) Drop support for Node v4, v6 and v8 ([@Turbo87](https://github.com/Turbo87)) 35 | 36 | #### :rocket: Enhancement 37 | * [#23](https://github.com/ef4/ember-set-body-class/pull/23) Update `ember-cli-babel` to v7.22.1 ([@Turbo87](https://github.com/Turbo87)) 38 | 39 | #### :bug: Bug Fix 40 | * [#12](https://github.com/ef4/ember-set-body-class/pull/12) util/get-dom: Fix copy/paste issue in the error message ([@Turbo87](https://github.com/Turbo87)) 41 | 42 | #### :memo: Documentation 43 | * [#9](https://github.com/ef4/ember-set-body-class/pull/9) Update Readme with info about Fastboot support ([@acorncom](https://github.com/acorncom)) 44 | 45 | #### :house: Internal 46 | * [#61](https://github.com/ef4/ember-set-body-class/pull/61) Add `release-it` to simplify the release process ([@Turbo87](https://github.com/Turbo87)) 47 | * [#60](https://github.com/ef4/ember-set-body-class/pull/60) CI: Add `release` job ([@Turbo87](https://github.com/Turbo87)) 48 | * [#57](https://github.com/ef4/ember-set-body-class/pull/57) Convert to native classes ([@Turbo87](https://github.com/Turbo87)) 49 | * [#26](https://github.com/ef4/ember-set-body-class/pull/26) ember-try: Use `ember-source-channel-url` for release channels ([@Turbo87](https://github.com/Turbo87)) 50 | * [#56](https://github.com/ef4/ember-set-body-class/pull/56) CI: Disable broken release channel scenarios ([@Turbo87](https://github.com/Turbo87)) 51 | * [#53](https://github.com/ef4/ember-set-body-class/pull/53) Replace unit/integration test with more application tests ([@Turbo87](https://github.com/Turbo87)) 52 | * [#52](https://github.com/ef4/ember-set-body-class/pull/52) Remove obsolete `getDOM()` utility ([@Turbo87](https://github.com/Turbo87)) 53 | * [#44](https://github.com/ef4/ember-set-body-class/pull/44) renovate: Enable automerging for semver-compatible updates ([@Turbo87](https://github.com/Turbo87)) 54 | * [#33](https://github.com/ef4/ember-set-body-class/pull/33) Use `prettier` for code formatting ([@Turbo87](https://github.com/Turbo87)) 55 | * [#31](https://github.com/ef4/ember-set-body-class/pull/31) Update ESLint and add Ember and Node plugins ([@Turbo87](https://github.com/Turbo87)) 56 | * [#30](https://github.com/ef4/ember-set-body-class/pull/30) tests: Remove unused test-helpers ([@Turbo87](https://github.com/Turbo87)) 57 | * [#29](https://github.com/ef4/ember-set-body-class/pull/29) Update `ember-cli-inject-live-reload` to v2.0.2 ([@Turbo87](https://github.com/Turbo87)) 58 | * [#28](https://github.com/ef4/ember-set-body-class/pull/28) Use `qunit-dom` for CSS class assertions ([@Turbo87](https://github.com/Turbo87)) 59 | * [#27](https://github.com/ef4/ember-set-body-class/pull/27) tests/acceptance: Use `setupApplicationTest()` helper ([@Turbo87](https://github.com/Turbo87)) 60 | * [#25](https://github.com/ef4/ember-set-body-class/pull/25) Remove unused/unnecessary dev dependencies ([@Turbo87](https://github.com/Turbo87)) 61 | * [#22](https://github.com/ef4/ember-set-body-class/pull/22) Use `ember-cli-fastboot-testing` for fastboot testing ([@Turbo87](https://github.com/Turbo87)) 62 | * [#21](https://github.com/ef4/ember-set-body-class/pull/21) Add explicit `ember-try` dev dependency ([@Turbo87](https://github.com/Turbo87)) 63 | * [#19](https://github.com/ef4/ember-set-body-class/pull/19) ember-try: Add additional LTS version scenarios ([@Turbo87](https://github.com/Turbo87)) 64 | * [#17](https://github.com/ef4/ember-set-body-class/pull/17) Migrate from `ember-cli-qunit` to `ember-qunit` ([@Turbo87](https://github.com/Turbo87)) 65 | * [#20](https://github.com/ef4/ember-set-body-class/pull/20) Update fastboot dependencies ([@Turbo87](https://github.com/Turbo87)) 66 | * [#16](https://github.com/ef4/ember-set-body-class/pull/16) Add GitHub Actions config ([@Turbo87](https://github.com/Turbo87)) 67 | * [#14](https://github.com/ef4/ember-set-body-class/pull/14) Migrate from `ember-cli-eslint` to `eslint` ([@Turbo87](https://github.com/Turbo87)) 68 | 69 | #### Committers: 2 70 | - David Baker ([@acorncom](https://github.com/acorncom)) 71 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 72 | 73 | --------------------------------------------------------------------------------