├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .template-lintrc.js ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── RELEASE.md ├── addon ├── .gitkeep └── components │ ├── radio-button-input.hbs │ ├── radio-button-input.js │ ├── radio-button.hbs │ └── radio-button.js ├── app ├── .gitkeep └── components │ ├── radio-button-input.js │ └── radio-button.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── testem.js ├── tests ├── .eslintrc.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── aria-example.js │ │ │ ├── autofocus-example.js │ │ │ ├── basic-example.js │ │ │ ├── disabled-example.js │ │ │ ├── id-and-class-example.js │ │ │ ├── legacy-actions-example.js │ │ │ ├── object-value-example.js │ │ │ ├── required-example.js │ │ │ ├── tab-index-example.js │ │ │ └── user-checked-class-example.js │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── components │ │ │ ├── .gitkeep │ │ │ ├── aria-example.hbs │ │ │ ├── autofocus-example.hbs │ │ │ ├── basic-example.hbs │ │ │ ├── disabled-example.hbs │ │ │ ├── id-and-class-example.hbs │ │ │ ├── object-value-example.hbs │ │ │ ├── required-example.hbs │ │ │ ├── tab-index-example.hbs │ │ │ └── user-checked-class-example.hbs │ │ │ └── index.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ └── person.js ├── index.html ├── integration │ ├── .gitkeep │ └── components │ │ └── radio-button-test.js ├── test-helper.js └── unit │ └── .gitkeep ├── vendor └── .gitkeep └── yarn.lock /.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 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.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 | .eslintcache 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /package.json.ember-try 23 | -------------------------------------------------------------------------------- /.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: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | rules: {}, 23 | overrides: [ 24 | // node files 25 | { 26 | files: [ 27 | './.eslintrc.js', 28 | './.prettierrc.js', 29 | './.template-lintrc.js', 30 | './ember-cli-build.js', 31 | './index.js', 32 | './testem.js', 33 | './blueprints/*/index.js', 34 | './config/**/*.js', 35 | './tests/dummy/config/**/*.js', 36 | ], 37 | parserOptions: { 38 | sourceType: 'script', 39 | }, 40 | env: { 41 | browser: false, 42 | node: true, 43 | }, 44 | plugins: ['node'], 45 | extends: ['plugin:node/recommended'], 46 | }, 47 | { 48 | // Test files: 49 | files: ['tests/**/*-test.{js,ts}'], 50 | extends: ['plugin:qunit/recommended'], 51 | }, 52 | ], 53 | }; 54 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - commit-message: 4 | include: "scope" 5 | prefix: "chore" 6 | directory: "/" 7 | open-pull-requests-limit: 10 8 | package-ecosystem: "npm" 9 | schedule: 10 | interval: "weekly" 11 | time: "10:00" 12 | versioning-strategy: "increase" 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: {} 9 | 10 | concurrency: 11 | group: ci-${{ github.head_ref || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | name: "Tests" 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Install Node 22 | uses: actions/setup-node@v2 23 | with: 24 | node-version: 12.x 25 | cache: yarn 26 | - name: Install Dependencies 27 | run: yarn install --frozen-lockfile 28 | - name: Lint 29 | run: yarn lint 30 | - name: Run Tests 31 | run: yarn test:ember 32 | 33 | floating: 34 | name: "Floating Dependencies" 35 | runs-on: ubuntu-latest 36 | 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: actions/setup-node@v2 40 | with: 41 | node-version: 12.x 42 | cache: yarn 43 | - name: Install Dependencies 44 | run: yarn install --no-lockfile 45 | - name: Run Tests 46 | run: yarn test:ember 47 | 48 | try-scenarios: 49 | name: ${{ matrix.try-scenario }} 50 | runs-on: ubuntu-latest 51 | needs: "test" 52 | 53 | strategy: 54 | fail-fast: false 55 | matrix: 56 | try-scenario: 57 | - ember-lts-3.20 58 | - ember-lts-3.24 59 | - ember-lts-3.28 60 | - ember-release 61 | - ember-beta 62 | - ember-canary 63 | - ember-classic 64 | - embroider-safe 65 | - embroider-optimized 66 | 67 | steps: 68 | - uses: actions/checkout@v2 69 | - name: Install Node 70 | uses: actions/setup-node@v2 71 | with: 72 | node-version: 12.x 73 | cache: yarn 74 | - name: Install Dependencies 75 | run: yarn install --frozen-lockfile 76 | - name: Run Tests 77 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }} 78 | -------------------------------------------------------------------------------- /.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 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /.eslintcache 16 | /connect.lock 17 | /coverage/ 18 | /libpeerconnection.log 19 | /npm-debug.log* 20 | /testem.log 21 | /yarn-error.log 22 | 23 | # ember-try 24 | /.node_modules.ember-try/ 25 | /bower.json.ember-try 26 | /package.json.ember-try 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.gitignore 18 | /.prettierignore 19 | /.prettierrc.js 20 | /.template-lintrc.js 21 | /.travis.yml 22 | /.watchmanconfig 23 | /bower.json 24 | /config/ember-try.js 25 | /CONTRIBUTING.md 26 | /ember-cli-build.js 27 | /testem.js 28 | /tests/ 29 | /yarn-error.log 30 | /yarn.lock 31 | .gitkeep 32 | 33 | # ember-try 34 | /.node_modules.ember-try/ 35 | /bower.json.ember-try 36 | /package.json.ember-try 37 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 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 | .eslintcache 17 | 18 | # ember-try 19 | /.node_modules.ember-try/ 20 | /bower.json.ember-try 21 | /package.json.ember-try 22 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | rules: { 6 | 'no-positive-tabindex': 'off', 7 | }, 8 | overrides: [ 9 | { 10 | files: ['tests/dummy/app/**/*.hbs'], 11 | rules: { 12 | 'no-inline-styles': 'off', 13 | 'require-input-label': 'off', 14 | }, 15 | }, 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## v3.0.0-beta.1 (2022-12-06) 3 | 4 | ## v3.0.0-beta.0 (2022-05-31) 5 | 6 | #### :boom: Breaking Change 7 | * [#119](https://github.com/yapplabs/ember-radio-button/pull/119) [BREAKING] Update dependencies, including ember-auto-import and add CI ([@lukemelia](https://github.com/lukemelia)) 8 | 9 | #### Committers: 4 10 | - Gabriel Cousin ([@GabrielCousin](https://github.com/GabrielCousin)) 11 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 12 | - Ray Cohen ([@raycohen](https://github.com/raycohen)) 13 | - Will Bagby ([@bagby](https://github.com/bagby)) 14 | 15 | ### 2.0.1 16 | - fix to use valid `aria-checked` attribute values 17 | 18 | ### 2.0.0 19 | - fixes `sendAction` deprecation warnings, using closure actions internally 20 | - removes support for Ember versions below 2.8 21 | - removes `labeled-radio-button` which was replaced by the block usage of `radio-button` 22 | 23 | ### 1.3.0 24 | - adds support for passing a closure action to the `changed` property 25 | 26 | ### 1.2.4 27 | - adds `aria-checked` attribute binding 28 | 29 | ### 1.2.3 30 | - adds `checkedClass` to override the default "checked" classname on the label when it is checked 31 | 32 | ### 1.2.2 33 | - adds `ariaDescribedby` 34 | 35 | ### 1.2.1 36 | - adds `ariaLabelledby` 37 | 38 | ### 1.2.0 39 | - README overhaul 40 | - adds `tabindex` and `autofocus` 41 | - use `Ember.isEqual` for comparison 42 | - updated examples in the test application to be implemented with components 43 | - fix for ember engine support 44 | 45 | ### 1.1.1 46 | - adds support for engines 47 | - Potential Breaking Change: The addon now uses templates from the addon folder. 48 | If you were supplying your own templates at the previous location 49 | (`app/templates/components/radio-button.hbs` for example) 50 | You will need to instead extend the provided component and explicitly 51 | supply your own template. 52 | 53 | ### 1.0.7 54 | 55 | - adds a `radioClass` property to put any classes you want on the input element 56 | - adds a `radioId` property to specify an id for the input element, allows 57 | associating non-wrapping label elements 58 | 59 | ### 1.0.6 60 | 61 | - Use `hasBlock` internally and remove deprecation warnings from 62 | accessing the component's `template` property 63 | 64 | ### 1.0.5 65 | 66 | - Improve IE8 Support 67 | 68 | ### 1.0.4 69 | 70 | - Fix putting additional classNames on the label 71 | 72 | ### 1.0.3 73 | 74 | - Put additional classNames on the label 75 | 76 | ### 1.0.2 77 | 78 | - Add `ember-radio-button` class to the label when used as a block 79 | 80 | ### 1.0.1 81 | 82 | - Put a `checked` classname on the label element when a block form 83 | radio-button is checked 84 | 85 | ### 1.0.0 86 | 87 | - Update to htmlbars 88 | - Rely on the `change` event, not `click` 89 | - Only `label` and `input` elements emitted, no more `span` 90 | 91 | ### 0.1.3 92 | 93 | - Add `required` and `name` bound attributes 94 | 95 | ### 0.1.2 96 | 97 | - Add `disabled` bound attribute 98 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-radio-button [![Build Status](https://travis-ci.org/yapplabs/ember-radio-button.svg?branch=master)](https://travis-ci.org/yapplabs/ember-radio-button) [![Ember Observer Score](https://emberobserver.com/badges/ember-radio-button.svg)](https://emberobserver.com/addons/ember-radio-button) 2 | 3 | This addon provides a `radio-button` component. 4 | 5 | A `radio-button` will be in a checked state when the `value` property matches the `groupValue` property. 6 | `value` should be unique per radio-button, while the same `groupValue` should be provided to each 7 | `radio-button` in the group. 8 | 9 | Clicking on a `radio-button` will set `groupValue` to its `value`. 10 | 11 | * Ember.js v3.20 or above 12 | * Ember CLI v3.20 or above 13 | * Node.js v12 or above 14 | 15 | ## Usage 16 | ### Block Form 17 | 18 | The block form emits a label wrapping the input element and any elements passed to the block. 19 | 20 | **Template:** 21 | 22 | ```javascript 23 | 28 | Blue 29 | 30 | 31 | /* results in */ 32 | 36 | ``` 37 | 38 | ### Non-block form 39 | 40 | If you want more control over the DOM, the non-block form only emits a single input element 41 | 42 | ```javascript 43 | 49 | 50 | /* results in */ 51 | 52 | ``` 53 | 54 | ### Examples in the test application 55 | 56 | [More example usage](https://github.com/yapplabs/ember-radio-button/tree/master/tests/dummy/app/components) can be seen in the test application. 57 | 58 | ## Supported Ember Versions 59 | 60 | | ember-radio-button version | supports | 61 | | -------------------------- | ----------- | 62 | | 3.x | Ember 3.16+ | 63 | | 2.x | Ember 2.8+ | 64 | | 1.x | Ember 1.11+ | 65 | 66 | ## Properties 67 | 68 | _Required:_ 69 | 70 | | name | type | description | 71 | | ---------- | ---- | ------------------------------------------------------------------------------------------------------------------- | 72 | | value | any | the unique value represented by the radio button | 73 | | groupValue | any | the value representing a radio group's current value. supply the same `groupValue` to every radio-button in a group | 74 | 75 | _Optional:_ 76 | 77 | | name | type | description | 78 | | --------------- | ------- | ---------------------------------------------------------------------------------------------------------------------- | 79 | | ariaDescribedby | string | applies an `aria-describedby` attribute to the input element | 80 | | ariaLabelledby | string | applies an `aria-labelledby` attribute to the input element | 81 | | autofocus | boolean | applies the `autofocus` property to the input element | 82 | | checkedClass | string | classname to apply to the `label` element when the input it wraps is checked. block form only. defaults to `"checked"` | 83 | | classNames | string | applies additional classnames to the `label` element (block form only) | 84 | | disabled | boolean | applies the `disabled` property to the input element | 85 | | name | string | applies the `name` property to the input element | 86 | | radioClass | string | applies additional classnames to the input element | 87 | | radioId | string | sets the `id` of the input element and the `for` property to the label element | 88 | | required | boolean | applies the `required` property to the input element | 89 | | tabindex | number | applies a `tabindex` property to the input element | 90 | 91 | _Actions:_ 92 | 93 | | name | description | 94 | | ------- | ------------------------------------------------------------------------ | 95 | | changed | fires when user interaction causes a radio-button to update `groupValue` | 96 | 97 | ## Installing 98 | 99 | `ember install ember-radio-button` 100 | 101 | ## Legacy Action Support 102 | 103 | A string can be supplied for the `changed` property to enable legacy `sendAction` style action propagation. 104 | 105 | ## Older versions of ember 106 | 107 | ember-radio-button 1.0.0+ requires using htmlbars. 108 | Applications not using htmlbars should use version 0.1.3 or the `pre-htmlbars` branch. 109 | 110 | ## Collaborating on this repo 111 | 112 | - `git clone ` this repository 113 | - `cd ember-radio-button` 114 | - `npm install` 115 | 116 | ## Running 117 | 118 | - `ember serve` 119 | - Visit your app at [http://localhost:4200](http://localhost:4200). 120 | 121 | ## Running Tests 122 | 123 | - `npm test` (Runs `ember try:each` to test your addon against multiple Ember versions) 124 | - `ember test` 125 | - `ember test --server` 126 | 127 | ## Building 128 | 129 | - `ember build` 130 | 131 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 132 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 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 | ## Preparation 8 | 9 | Since the majority of the actual release process is automated, the primary 10 | remaining task prior to releasing is confirming that all pull requests that 11 | have been merged since the last release have been labeled with the appropriate 12 | `lerna-changelog` labels and the titles have been updated to ensure they 13 | represent something that would make sense to our users. Some great information 14 | on why this is important can be found at 15 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 16 | guiding principle here is that changelogs are for humans, not machines. 17 | 18 | When reviewing merged PR's the labels to be used are: 19 | 20 | * breaking - Used when the PR is considered a breaking change. 21 | * enhancement - Used when the PR adds a new feature or enhancement. 22 | * bug - Used when the PR fixes a bug included in a previous release. 23 | * documentation - Used when the PR adds or updates documentation. 24 | * internal - Used for internal changes that still require a mention in the 25 | changelog/release notes. 26 | 27 | ## Release 28 | 29 | Once the prep work is completed, the actual release is straight forward: 30 | 31 | * First, ensure that you have installed your projects dependencies: 32 | 33 | ```sh 34 | yarn install 35 | ``` 36 | 37 | * Second, ensure that you have obtained a 38 | [GitHub personal access token][generate-token] with the `repo` scope (no 39 | other permissions are needed). Make sure the token is available as the 40 | `GITHUB_AUTH` environment variable. 41 | 42 | For instance: 43 | 44 | ```bash 45 | export GITHUB_AUTH=abc123def456 46 | ``` 47 | 48 | [generate-token]: https://github.com/settings/tokens/new?scopes=repo&description=GITHUB_AUTH+env+variable 49 | 50 | * And last (but not least 😁) do your release. 51 | 52 | ```sh 53 | npx release-it 54 | ``` 55 | 56 | [release-it](https://github.com/release-it/release-it/) manages the actual 57 | release process. It will prompt you to to choose the version number after which 58 | you will have the chance to hand tweak the changelog to be used (for the 59 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 60 | pushing the tag and commits, etc. 61 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/addon/.gitkeep -------------------------------------------------------------------------------- /addon/components/radio-button-input.hbs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addon/components/radio-button-input.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { once } from '@ember/runloop'; 3 | import { action } from '@ember/object'; 4 | 5 | export default class RadioButtonInputComponent extends Component { 6 | get checkedStr() { 7 | const checked = this.args.checked; 8 | 9 | if (typeof checked === 'boolean') { 10 | return checked.toString(); 11 | } 12 | 13 | return null; 14 | } 15 | 16 | @action change() { 17 | if (this.args.groupValue !== this.args.value) { 18 | // this.set('groupValue', value); 19 | once(this.args, 'changed', this.args.value); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /addon/components/radio-button.hbs: -------------------------------------------------------------------------------- 1 | {{#if (has-block)}} 2 | 20 | {{else}} 21 | 36 | {{/if}} 37 | -------------------------------------------------------------------------------- /addon/components/radio-button.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { isEqual } from '@ember/utils'; 4 | 5 | export default class RadioButtonComponent extends Component { 6 | // value - passed in, required, the value for this radio button 7 | // groupValue - passed in, required, the currently selected value 8 | 9 | // optionally passed in: 10 | // disabled - boolean 11 | // required - boolean 12 | // name - string 13 | // radioClass - string 14 | // radioId - string 15 | // ariaLabelledby - string 16 | // ariaDescribedby - string 17 | 18 | get joinedClassNames() { 19 | const classNames = this.args.classNames; 20 | if (classNames && classNames.length && classNames.join) { 21 | return classNames.join(' '); 22 | } 23 | return classNames; 24 | } 25 | 26 | get checkedClass() { 27 | return this.args.checkedClass || 'checked'; 28 | } 29 | 30 | get checked() { 31 | return isEqual(this.args.groupValue, this.args.value); 32 | } 33 | 34 | @action changed(newValue) { 35 | let changedAction = this.args.changed; 36 | 37 | // providing a closure action is optional 38 | if (changedAction) { 39 | changedAction(newValue); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/app/.gitkeep -------------------------------------------------------------------------------- /app/components/radio-button-input.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-radio-button/components/radio-button-input'; 2 | -------------------------------------------------------------------------------- /app/components/radio-button.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-radio-button/components/radio-button'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | useYarn: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-3.20', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~3.20.5', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-3.24', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~3.24.3', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-lts-3.28', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': '~3.28.9', 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-release', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': await getChannelURL('release'), 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-beta', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('beta'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-canary', 52 | npm: { 53 | devDependencies: { 54 | 'ember-source': await getChannelURL('canary'), 55 | }, 56 | }, 57 | }, 58 | { 59 | name: 'ember-default-with-jquery', 60 | env: { 61 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 62 | 'jquery-integration': true, 63 | }), 64 | }, 65 | npm: { 66 | devDependencies: { 67 | '@ember/jquery': '^1.1.0', 68 | }, 69 | }, 70 | }, 71 | { 72 | name: 'ember-classic', 73 | env: { 74 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 75 | 'application-template-wrapper': true, 76 | 'default-async-observers': false, 77 | 'template-only-glimmer-components': false, 78 | }), 79 | }, 80 | npm: { 81 | ember: { 82 | edition: 'classic', 83 | }, 84 | }, 85 | }, 86 | embroiderSafe(), 87 | embroiderOptimized(), 88 | ], 89 | }; 90 | }; 91 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (/* environment, appConfig */) { 4 | return {}; 5 | }; 6 | -------------------------------------------------------------------------------- /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 | const { maybeEmbroider } = require('@embroider/test-setup'); 18 | return maybeEmbroider(app, { 19 | skipBabel: [ 20 | { 21 | package: 'qunit', 22 | }, 23 | ], 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-radio-button", 3 | "version": "3.0.0-beta.1", 4 | "description": "This addon provides a RadioButton component", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "repository": { 9 | "type": "git", 10 | "url": "git@github.com:yapplabs/ember-radio-button.git" 11 | }, 12 | "license": "MIT", 13 | "author": "", 14 | "directories": { 15 | "doc": "doc", 16 | "test": "tests" 17 | }, 18 | "scripts": { 19 | "build": "ember build --environment=production", 20 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 21 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 22 | "lint:hbs": "ember-template-lint .", 23 | "lint:hbs:fix": "ember-template-lint . --fix", 24 | "lint:js": "eslint . --cache", 25 | "lint:js:fix": "eslint . --fix", 26 | "start": "ember serve", 27 | "test": "npm-run-all lint test:*", 28 | "test:ember": "ember test", 29 | "test:ember-compatibility": "ember try:each" 30 | }, 31 | "dependencies": { 32 | "ember-cli-babel": "^7.26.11", 33 | "ember-cli-htmlbars": "^5.7.1", 34 | "webpack": "^5.72.1" 35 | }, 36 | "devDependencies": { 37 | "@ember/optional-features": "^2.0.0", 38 | "@ember/test-helpers": "^2.4.2", 39 | "@embroider/test-setup": "^1.7.1", 40 | "@glimmer/component": "^1.1.2", 41 | "@glimmer/tracking": "^1.1.2", 42 | "babel-eslint": "^10.1.0", 43 | "broccoli-asset-rev": "^3.0.0", 44 | "ember-auto-import": "^2.4.2", 45 | "ember-cli": "~3.28.0", 46 | "ember-cli-dependency-checker": "^3.3.1", 47 | "ember-cli-inject-live-reload": "^2.1.0", 48 | "ember-cli-sri": "^2.1.1", 49 | "ember-cli-terser": "^4.0.2", 50 | "ember-disable-prototype-extensions": "^1.1.3", 51 | "ember-export-application-global": "^2.0.1", 52 | "ember-load-initializers": "^2.1.2", 53 | "ember-maybe-import-regenerator": "^1.0.0", 54 | "ember-page-title": "^7.0.0", 55 | "ember-qunit": "^5.1.5", 56 | "ember-resolver": "^8.0.3", 57 | "ember-source": "~3.28.0", 58 | "ember-source-channel-url": "^3.0.0", 59 | "ember-template-lint": "^3.6.0", 60 | "ember-try": "^1.4.0", 61 | "ember-welcome-page": "^6.2.0", 62 | "eslint": "^7.32.0", 63 | "eslint-config-prettier": "^8.3.0", 64 | "eslint-plugin-ember": "^10.5.4", 65 | "eslint-plugin-node": "^11.1.0", 66 | "eslint-plugin-prettier": "^3.4.1", 67 | "eslint-plugin-qunit": "^6.2.0", 68 | "loader.js": "^4.7.0", 69 | "npm-run-all": "^4.1.5", 70 | "prettier": "^2.3.2", 71 | "qunit": "^2.16.0", 72 | "qunit-dom": "^2.0.0", 73 | "release-it": "^14.2.1", 74 | "release-it-lerna-changelog": "^3.1.0" 75 | }, 76 | "engines": { 77 | "node": "12.* || 14.* || >= 16" 78 | }, 79 | "volta": { 80 | "node": "14.19.3", 81 | "yarn": "1.22.18" 82 | }, 83 | "publishConfig": { 84 | "registry": "https://registry.npmjs.org" 85 | }, 86 | "ember": { 87 | "edition": "octane" 88 | }, 89 | "ember-addon": { 90 | "configPath": "tests/dummy/config" 91 | }, 92 | "release-it": { 93 | "plugins": { 94 | "release-it-lerna-changelog": { 95 | "infile": "CHANGELOG.md", 96 | "launchEditor": false 97 | } 98 | }, 99 | "git": { 100 | "tagName": "v${version}" 101 | }, 102 | "github": { 103 | "release": true, 104 | "tokenRef": "GITHUB_AUTH" 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /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_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | 'use strict'; 3 | 4 | module.exports = { 5 | env: { 6 | embertest: true, 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /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 'dummy/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 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/components/aria-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class AriaExample extends Component { 6 | @tracked color = 'red'; 7 | 8 | @action colorChanged(color) { 9 | this.color = color; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/dummy/app/components/autofocus-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class AutofocusExample extends Component { 6 | @tracked color = 'green'; 7 | 8 | @action colorChanged(color) { 9 | this.color = color; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/dummy/app/components/basic-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class BasicExample extends Component { 6 | @tracked color = 'green'; 7 | 8 | @action colorChanged(color) { 9 | this.color = color; 10 | window.alert(`Color changed to ${color}`); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/disabled-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class DisabledExample extends Component { 6 | @tracked number = 'one'; 7 | @tracked numbersDisabled = true; 8 | 9 | @action colorChanged(color) { 10 | this.color = color; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/id-and-class-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class IdAndClassExample extends Component { 6 | @tracked color = 'purple'; 7 | 8 | @action colorChanged(color) { 9 | this.color = color; 10 | window.alert(`Color changed to ${color}`); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/legacy-actions-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class LegacyActionsExample extends Component { 6 | @tracked color = 'maroon'; 7 | 8 | @action colorChanged(color) { 9 | this.color = color; 10 | window.alert(`Color changed to ${color}`); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/object-value-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import EmberObject from '@ember/object'; 3 | 4 | export default class ObjectValueExample extends Component { 5 | greenObject = { name: 'green object' }; 6 | 7 | blueObject = { name: 'blue object' }; 8 | 9 | reservation = EmberObject.create({ 10 | number: 'one', 11 | }); 12 | 13 | constructor() { 14 | super(...arguments); 15 | this.selectedColorObject = this.blueObject; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/dummy/app/components/required-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { tracked } from '@glimmer/tracking'; 3 | 4 | export default class RequiredExample extends Component { 5 | @tracked noDefault = ''; 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/app/components/tab-index-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { tracked } from '@glimmer/tracking'; 3 | 4 | export default class TabIndexExample extends Component { 5 | @tracked color = 'blue'; 6 | @tracked size = 'small'; 7 | } 8 | -------------------------------------------------------------------------------- /tests/dummy/app/components/user-checked-class-example.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class UserCheckedClassExample extends Component { 6 | @tracked color = 'green'; 7 | 8 | @action colorChanged(color) { 9 | this.color = color; 10 | window.alert(`Color changed to ${color}`); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /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/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/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/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 20px; 3 | } 4 | 5 | input:focus { 6 | outline: 2px dashed red; 7 | } 8 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

radio-button component

2 | 3 | {{outlet}} 4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/aria-example.hbs: -------------------------------------------------------------------------------- 1 |

Aria Labelled by

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

12 | This color is created by combining Blue and Red. 13 |

14 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/autofocus-example.hbs: -------------------------------------------------------------------------------- 1 |

Autofocus

2 | 3 | 4 | green (autofocused) 5 | 6 | 7 | red 8 | 9 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/basic-example.hbs: -------------------------------------------------------------------------------- 1 |

Basic Usage

2 | 3 | Selected Color: {{this.color}} 4 |

5 | 6 | Green 7 | 8 | 9 | Blue 10 | 11 |

12 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/disabled-example.hbs: -------------------------------------------------------------------------------- 1 |

Disabled flag

2 | Numbers Disabled: {{this.numbersDisabled}} 3 | Number: {{this.number}} 4 |

5 | 6 | Disabled 7 | 8 | 9 | Enabled 10 | 11 |

12 |

13 | 14 | One 15 | 16 | 17 | Two 18 | 19 | 20 | Three 21 | 22 |

23 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/id-and-class-example.hbs: -------------------------------------------------------------------------------- 1 |

Using radioId and radioClass properties

2 | Selected Color: {{this.color}} 3 |

4 | 5 | Purple 6 | 7 | 8 | Orange 9 | 10 |

11 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/object-value-example.hbs: -------------------------------------------------------------------------------- 1 |

Using objects as radio button values

2 | 3 | selectedColorObject.name: {{this.selectedColorObject.name}} 4 |

5 | 6 | Green Object 7 | 8 | 9 | Blue Object 10 | 11 |

12 | 13 |

Chained group value properties

14 | 15 |
16 | Model.number: {{this.reservation.number}} 17 |
18 |
19 | 20 | One 21 | 22 | 23 | Two 24 | 25 | 26 | Three 27 | 28 |
29 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/required-example.hbs: -------------------------------------------------------------------------------- 1 |

Required

2 | No Default: {{this.noDefault}} 3 |

4 |

5 | 6 | Disabled 7 | 8 | 9 | Enabled 10 |
11 | 12 |
13 |

14 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/tab-index-example.hbs: -------------------------------------------------------------------------------- 1 |

Tab Index

2 | Selected Color: {{this.color}} 3 |
4 | Selected Size: {{this.size}} 5 |
6 |

7 |

8 | Use the arrow keys to move between radio buttons with the same `name`. 9 | The tab key moves between groups of radio buttons and other input elements. 10 |
11 |
12 | See the w3 RadioButton wiki for details 13 |

14 |
15 |
16 | Tab index 4: 17 |
18 | 19 | Red 20 | 21 |
22 | 23 | Blue 24 | 25 |
26 | 27 | Green 28 | 29 |
30 |
31 |
32 | Tab index 2: 33 |
34 | Small
35 | Medium
36 | Large
37 |
38 |
39 |
40 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/user-checked-class-example.hbs: -------------------------------------------------------------------------------- 1 |

Using a different 'checked' class

2 | Selected Color: {{this.color}} 3 |

4 | 5 | Green 6 | 7 | 8 | Blue 9 | 10 |

11 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "3.28.0", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--welcome", 15 | "--yarn" 16 | ] 17 | } 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /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. EMBER_NATIVE_DECORATOR_SUPPORT: 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 | -------------------------------------------------------------------------------- /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/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | // Ember's browser support policy is changing, and IE11 support will end in 10 | // v4.0 onwards. 11 | // 12 | // See https://deprecations.emberjs.com/v3.x#toc_3-0-browser-support-policy 13 | // 14 | // If you need IE11 support on a version of Ember that still offers support 15 | // for it, uncomment the code block below. 16 | // 17 | // const isCI = Boolean(process.env.CI); 18 | // const isProduction = process.env.EMBER_ENV === 'production'; 19 | // 20 | // if (isCI || isProduction) { 21 | // browsers.push('ie 11'); 22 | // } 23 | 24 | module.exports = { 25 | browsers, 26 | }; 27 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/person.js: -------------------------------------------------------------------------------- 1 | export class Person { 2 | constructor({ name, ssn }) { 3 | this.name = name; 4 | this.ssn = ssn; 5 | } 6 | 7 | isEqual(other) { 8 | return this.ssn == other.ssn; 9 | } 10 | } 11 | 12 | const matchingSSN = '123-45-6789'; 13 | 14 | export const alice = new Person({ name: 'Alice', ssn: matchingSSN }); 15 | export const alice2 = new Person({ name: 'Alice 2', ssn: matchingSSN }); 16 | export const bob = new Person({ name: 'Bob', ssn: '999-99-9999' }); 17 | -------------------------------------------------------------------------------- /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 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{content-for "body-footer"}} 38 | {{content-for "test-body-footer"}} 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/integration/components/radio-button-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render, triggerEvent } from '@ember/test-helpers'; 4 | import { hbs } from 'ember-cli-htmlbars'; 5 | import { alice, alice2, bob } from '../../helpers/person'; 6 | 7 | module('Integration | Components | Radio Button', function (hooks) { 8 | setupRenderingTest(hooks); 9 | 10 | test('begins checked when groupValue matches value', async function (assert) { 11 | assert.expect(2); 12 | 13 | await render(hbs` 14 | 18 | `); 19 | 20 | assert.dom('input').isChecked(); 21 | assert.dom('input').hasAttribute('aria-checked', 'true'); 22 | }); 23 | 24 | test('it updates when clicked, and triggers the `changed` action', async function (assert) { 25 | assert.expect(7); 26 | 27 | let changedActionCallCount = 0; 28 | 29 | this.set('groupValue', 'initial-group-value'); 30 | 31 | this.set('changed', (newValue) => { 32 | changedActionCallCount++; 33 | assert.strictEqual(newValue, 'component-value', 'updates groupValue'); 34 | this.set('groupValue', newValue); 35 | }); 36 | 37 | await render(hbs` 38 | 43 | `); 44 | 45 | assert.strictEqual(changedActionCallCount, 0); 46 | assert.dom('input').isNotChecked(); 47 | assert 48 | .dom('input') 49 | .hasAttribute( 50 | 'aria-checked', 51 | 'false', 52 | 'aria-checked property starts false' 53 | ); 54 | 55 | await triggerEvent('input', 'click'); 56 | 57 | assert.dom('input').isChecked('updates element property'); 58 | assert 59 | .dom('input') 60 | .hasAttribute('aria-checked', 'true', 'updates aria-checked property'); 61 | 62 | assert.strictEqual(changedActionCallCount, 1); 63 | }); 64 | 65 | test('it updates when the browser change event is fired', async function (assert) { 66 | assert.expect(5); 67 | let changedActionCallCount = 0; 68 | this.set('changed', function (newValue) { 69 | changedActionCallCount++; 70 | assert.strictEqual(newValue, 'component-value', 'updates groupValue'); 71 | }); 72 | 73 | this.set('groupValue', 'initial-group-value'); 74 | 75 | await render(hbs` 76 | 81 | `); 82 | 83 | assert.strictEqual(changedActionCallCount, 0); 84 | assert.dom('input').isNotChecked(); 85 | 86 | await triggerEvent('input', 'click'); 87 | 88 | assert.dom('input').isChecked('updates DOM property'); 89 | 90 | assert.strictEqual(changedActionCallCount, 1); 91 | }); 92 | 93 | test('it gives the label of a wrapped checkbox a `checked` className', async function (assert) { 94 | assert.expect(4); 95 | 96 | this.set('groupValue', 'initial-group-value'); 97 | this.set('value', 'component-value'); 98 | 99 | await render(hbs` 100 | 105 | Blue 106 | 107 | `); 108 | 109 | assert.dom('label').doesNotHaveClass('checked'); 110 | 111 | this.set('value', 'initial-group-value'); 112 | 113 | assert.dom('label').hasClass('checked'); 114 | assert.dom('label').hasClass('ember-radio-button'); 115 | assert.dom('label').hasClass('blue-radio'); 116 | }); 117 | 118 | test('providing `checkedClass` gives the label a custom classname when the radio is checked', async function (assert) { 119 | assert.expect(5); 120 | 121 | this.set('groupValue', 'initial-group-value'); 122 | this.set('value', 'component-value'); 123 | 124 | await render(hbs` 125 | 131 | Blue 132 | 133 | `); 134 | 135 | assert 136 | .dom('label') 137 | .doesNotHaveClass( 138 | 'my-custom-class', 139 | 'does not have user-provided checkedClass' 140 | ); 141 | 142 | this.set('value', 'initial-group-value'); 143 | 144 | assert 145 | .dom('label') 146 | .doesNotHaveClass('checked', 'does not have the `checked` class'); 147 | assert 148 | .dom('label') 149 | .hasClass('my-custom-class', 'has user-provided checkedClass'); 150 | assert 151 | .dom('label') 152 | .hasClass('ember-radio-button', 'has class `ember-radio-button`'); 153 | assert.dom('label').hasClass('blue-radio', 'has class `blue-radio`'); 154 | }); 155 | 156 | test('it updates when setting `value`', async function (assert) { 157 | assert.expect(3); 158 | 159 | this.set('groupValue', 'initial-group-value'); 160 | this.set('value', 'component-value'); 161 | 162 | await render(hbs` 163 | 167 | `); 168 | 169 | assert.dom('input').isNotChecked(); 170 | 171 | this.set('value', 'initial-group-value'); 172 | 173 | assert.dom('input').isChecked(); 174 | 175 | this.set('value', 'component-value'); 176 | 177 | assert.dom('input').isNotChecked(); 178 | }); 179 | 180 | test('begins disabled when disabled is true', async function (assert) { 181 | assert.expect(1); 182 | 183 | await render(hbs``); 184 | 185 | assert.dom('input').isDisabled(); 186 | }); 187 | 188 | test('updates disabled when the disabled attribute changes', async function (assert) { 189 | this.set('isDisabled', false); 190 | 191 | await render(hbs``); 192 | 193 | assert.dom('input').isNotDisabled(); 194 | 195 | this.set('isDisabled', true); 196 | 197 | assert.dom('input').isDisabled(); 198 | 199 | this.set('isDisabled', false); 200 | 201 | assert.dom('input').isNotDisabled(); 202 | }); 203 | 204 | test('begins with the `required` and `name` attributes when specified', async function (assert) { 205 | await render(hbs``); 206 | 207 | assert.dom('input').isRequired(); 208 | assert.dom('input').hasAttribute('name', 'colors'); 209 | }); 210 | 211 | test('updates the `required` attribute when the property changes', async function (assert) { 212 | this.set('isRequired', false); 213 | 214 | await render(hbs``); 215 | 216 | assert.dom('input').isNotRequired(); 217 | 218 | this.set('isRequired', true); 219 | 220 | assert.dom('input').isRequired(); 221 | 222 | this.set('isRequired', false); 223 | 224 | assert.dom('input').isNotRequired(); 225 | }); 226 | 227 | test('updates the `name` attribute when the property changes', async function (assert) { 228 | this.set('name', undefined); 229 | 230 | await render(hbs``); 231 | 232 | assert.dom('input').hasNoAttribute('name'); 233 | 234 | this.set('name', 'colors'); 235 | 236 | assert.dom('input').hasAttribute('name', 'colors'); 237 | }); 238 | 239 | test('uses a layout, tagName=label, when given a template', async function (assert) { 240 | await render(hbs`Red`); 241 | 242 | assert.dom('label').hasText('Red'); 243 | assert.dom('input[type="radio"]').exists({ count: 1 }); 244 | assert.dom('.ember-radio-button').exists(); 245 | }); 246 | 247 | test('it binds attributes only to the input when used as a block', async function (assert) { 248 | await render(hbs` 249 | 256 | Blue 257 | 258 | `); 259 | 260 | assert.dom('.ember-radio-button').hasTagName('label', 'is a label'); 261 | assert.dom('.ember-radio-button').isNotChecked(); 262 | 263 | assert 264 | .dom('.ember-radio-button') 265 | .hasNoAttribute('name', 'does not have name set'); 266 | assert 267 | .dom('.ember-radio-button') 268 | .hasNoAttribute('required', 'is not required'); 269 | assert 270 | .dom('.ember-radio-button') 271 | .hasNoAttribute('type', 'does not have attribute type'); 272 | assert 273 | .dom('.ember-radio-button') 274 | .hasNoAttribute('value', 'does not have attribute value'); 275 | 276 | const inputSelector = '.ember-radio-button input[type="radio"]'; 277 | 278 | assert.dom(inputSelector).exists(); 279 | assert.dom(inputSelector).isChecked(); 280 | assert.dom(inputSelector).isDisabled(); 281 | assert.dom(inputSelector).hasAttribute('name', 'some-name', 'has name set'); 282 | assert.dom(inputSelector).isRequired('is required'); 283 | assert.dom(inputSelector).hasValue('blue'); 284 | }); 285 | 286 | test('it checks the input when the label is clicked and has a `for` attribute', async function (assert) { 287 | assert.expect(4); 288 | 289 | this.set('value', 'component-value'); 290 | 291 | await render(hbs` 292 | 298 | Green 299 | 300 | `); 301 | 302 | await triggerEvent('label', 'click'); 303 | 304 | assert 305 | .dom('label') 306 | .hasAttribute( 307 | 'for', 308 | 'green-0', 309 | 'the label has the correct `for` attribute' 310 | ); 311 | assert 312 | .dom('input') 313 | .hasAttribute( 314 | 'id', 315 | 'green-0', 316 | 'the input has the correct `id` attribute' 317 | ); 318 | 319 | assert.dom('input').hasClass('my-radio-class'); 320 | assert.dom('input').isChecked('clicking the label checks the radio'); 321 | }); 322 | 323 | test('it updates when setting `value` with isEqual', async function (assert) { 324 | assert.expect(3); 325 | 326 | this.set('groupValue', alice); 327 | this.set('value', bob); 328 | 329 | await render(hbs` 330 | 334 | `); 335 | 336 | assert.dom('input').isNotChecked(); 337 | 338 | this.set('value', alice2); 339 | 340 | assert.dom('input').isChecked(); 341 | 342 | this.set('value', bob); 343 | 344 | assert.dom('input').isNotChecked(); 345 | }); 346 | 347 | test('it binds `aria-labelledby` when specified', async function (assert) { 348 | assert.expect(1); 349 | 350 | this.set('ariaLabelledby', 'green-label'); 351 | 352 | await render(hbs` 353 | 358 | Green 359 | 360 | `); 361 | 362 | assert.dom('input').hasAttribute('aria-labelledby', 'green-label'); 363 | }); 364 | 365 | test('it binds `aria-describedby` when specified', async function (assert) { 366 | assert.expect(1); 367 | 368 | this.set('ariaDescribedby', 'green-label'); 369 | 370 | await render(hbs` 371 | 376 | Green 377 | 378 | `); 379 | 380 | assert.dom('input').hasAttribute('aria-describedby', 'green-label'); 381 | }); 382 | }); 383 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-radio-button/732619a8557bd0ecc5f5e769426940bde1c52c1b/vendor/.gitkeep --------------------------------------------------------------------------------