diff --git a/addons/rose/addon/components/rose/checkbox.js b/addons/rose/addon/components/rose/checkbox.js deleted file mode 100644 index 498f01042..000000000 --- a/addons/rose/addon/components/rose/checkbox.js +++ /dev/null @@ -1,21 +0,0 @@ -import Component from '@ember/component'; -import layout from '../../templates/components/rose/checkbox'; - -/** - A simple ember input `` element with associated `label`. - - @example - - Label - Name - Form - Value - Error - Checked - Disabled - Disabled + Checked -**/ - -export default Component.extend({ - layout, -}); diff --git a/addons/rose/addon/components/rose/form/checkbox.js b/addons/rose/addon/components/rose/form/checkbox.js new file mode 100644 index 000000000..5b94c0a1d --- /dev/null +++ b/addons/rose/addon/components/rose/form/checkbox.js @@ -0,0 +1,21 @@ +import Component from '@ember/component'; +import layout from '../../../templates/components/rose/form/checkbox'; + +/** + * A simple HTML `` element. + * + * @example + * + */ +export default Component.extend({ + layout, + + tagName: '', +}); diff --git a/addons/rose/addon/styles/rose/components/_checkbox.scss b/addons/rose/addon/styles/rose/components/_checkbox.scss deleted file mode 100644 index cdc844774..000000000 --- a/addons/rose/addon/styles/rose/components/_checkbox.scss +++ /dev/null @@ -1,82 +0,0 @@ -@use '../variables/sizing'; -@use '../utilities/type' as typography; - -$checkbox-check-height: sizing.rems(xs) - sizing.rems(xxxs); -$checkbox-check-size: sizing.rems(xxxxs) + (sizing.rems(xxxs) / 3); -$checkbox-check-left-position: sizing.rems(xxs) - (sizing.rems(xxs) / 3); - -.rose-checkbox { - @include typography.type(m); - color: var(--stark); - background-color: var(--subtle); - - .rose-checkbox-input { - -webkit-appearance: none; - appearance: none; - display: inline-block; - position: relative; - padding: 0; - margin: 0; - border: sizing.rems(xxxxs) solid var(--ui-border); - border-radius: sizing.rems(xxxs); - width: 14px; - height: 14px; - background-color: var(--subtle); - - &::before { - content: ''; - position: absolute; - left: $checkbox-check-left-position; - width: sizing.rems(xxxs); - height: $checkbox-check-height; - border: solid; - border-width: 0 $checkbox-check-size $checkbox-check-size 0; - border-color: var(--info); - border-radius: sizing.rems(xxxxs); - -webkit-transform: rotate(45deg); - transform: rotate(45deg); - visibility: hidden; - } - - &:checked { - border-color: var(--info); - } - - &:disabled { - cursor: not-allowed; - border-color: var(--ui-border); - background-color: var(--ui-gray-subtler-4); - - &::before { - border-color: var(--ui-border); - } - } - - &:checked::before { - visibility: visible; - } - - &:focus { - outline: none; - border-color: var(--info); - } - - &:hover { - border-color: var(--ui-gray); - } - } - - &.error { - .rose-checkbox-input { - border-color: var(--failure); - - &:focus { - border-color: var(--info); - } - - &:disabled { - border-color: var(--ui-border); - } - } - } -} diff --git a/addons/rose/addon/styles/rose/components/_index.scss b/addons/rose/addon/styles/rose/components/_index.scss index 2667fb1c7..dd972080b 100644 --- a/addons/rose/addon/styles/rose/components/_index.scss +++ b/addons/rose/addon/styles/rose/components/_index.scss @@ -1,3 +1,2 @@ @import 'button'; @import 'form'; -@import 'checkbox'; diff --git a/addons/rose/addon/styles/rose/components/form/_checkbox.scss b/addons/rose/addon/styles/rose/components/form/_checkbox.scss new file mode 100644 index 000000000..4b826841e --- /dev/null +++ b/addons/rose/addon/styles/rose/components/form/_checkbox.scss @@ -0,0 +1,122 @@ +@use '../../variables/sizing'; +@use '../../utilities/type'; + +.rose-form-checkbox { + position: relative; + margin-bottom: sizing.rems(xs); + color: var(--ui-gray); + + .rose-form-checkbox-label { + // label styles + @include type.type(s, semibold); + color: currentColor; + padding-left: sizing.rems(l); + + // custom checkbox styles + &::before { + position: absolute; + top: sizing.rems(xxxs); + left: 0; + display: block; + width: sizing.rems(m); + height: sizing.rems(m); + pointer-events: none; + content: ' '; + border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2); + border-radius: sizing.rems(xxxs); + } + + &::after { + position: absolute; + top: -#{sizing.rems(xxxxs)}; + left: -#{sizing.rems(xxxs) + sizing.rems(xxxxs)}; + display: block; + width: sizing.rems(l); + height: sizing.rems(l); + transform: scale(0.9); + pointer-events: none; + content: ' '; + background: no-repeat 50%/50% 50%; + } + } + + .rose-form-checkbox-field { + // mostly this just visually hides the native checkbox + position: absolute; + left: 0; + z-index: -1; + width: sizing.rems(m); + height: sizing.rems(m); + opacity: 0; + + // Visual states for custom checkbox. + // Yeah. It's complicated. + &:not(:disabled) { + &:hover { + ~ .rose-form-checkbox-label { + &::before { + border-color: var(--ui-gray); + } + } + } + + &:focus { + ~ .rose-form-checkbox-label { + &::before { + border-color: var(--info); + } + } + } + + &:checked { + ~ .rose-form-checkbox-label { + &::before { + border-color: var(--info); + } + &::after { + background-image: url("data:image/svg+xml,"); + } + } + } + } + + &:disabled { + ~ .rose-form-checkbox-label { + &::before { + border-color: var(--ui-gray-subtler-2); + background-color: var(--ui-gray-subtler-4); + } + } + + &:checked { + ~ .rose-form-checkbox-label { + &::after { + background-image: url("data:image/svg+xml,"); + } + } + } + } + } + + &.error { + color: var(--failure); + + .rose-form-checkbox-field { + &:not(:disabled):not(:focus) { + ~ .rose-form-checkbox-label { + &::before { + border-color: var(--failure); + } + } + + &:checked { + ~ .rose-form-checkbox-label { + &::after { + background-image: url("data:image/svg+xml,"); + } + } + } + } + } + } +} diff --git a/addons/rose/addon/styles/rose/components/form/_index.scss b/addons/rose/addon/styles/rose/components/form/_index.scss index fdc60b1cd..55ce75517 100644 --- a/addons/rose/addon/styles/rose/components/form/_index.scss +++ b/addons/rose/addon/styles/rose/components/form/_index.scss @@ -1 +1,2 @@ @import 'input'; +@import 'checkbox'; diff --git a/addons/rose/addon/styles/rose/variables/_sizing.scss b/addons/rose/addon/styles/rose/variables/_sizing.scss index 93ebe4da7..db29d6a96 100644 --- a/addons/rose/addon/styles/rose/variables/_sizing.scss +++ b/addons/rose/addon/styles/rose/variables/_sizing.scss @@ -23,7 +23,7 @@ $size-coefficients: ( // 4 xxxs: 0.125, // 2 - xxxxs: 0.0625 // 1,,, + xxxxs: 0.0625 // 1,,,, ); /** diff --git a/addons/rose/addon/styles/rose/variables/_typography.scss b/addons/rose/addon/styles/rose/variables/_typography.scss index beaf59a5a..356954b3e 100644 --- a/addons/rose/addon/styles/rose/variables/_typography.scss +++ b/addons/rose/addon/styles/rose/variables/_typography.scss @@ -40,7 +40,7 @@ $font-sizes: ( // 20 h3: sizing.size(m), // 16 - button: sizing.size(m) * 0.8125 // 13,,, + button: sizing.size(m) * 0.8125 // 13,,,, ); // Line height expressed as ratio. diff --git a/addons/rose/addon/templates/components/rose/checkbox.hbs b/addons/rose/addon/templates/components/rose/checkbox.hbs deleted file mode 100644 index 3037e7218..000000000 --- a/addons/rose/addon/templates/components/rose/checkbox.hbs +++ /dev/null @@ -1,15 +0,0 @@ -
- - - -
diff --git a/addons/rose/addon/templates/components/rose/form/checkbox.hbs b/addons/rose/addon/templates/components/rose/form/checkbox.hbs new file mode 100644 index 000000000..ab2dec73b --- /dev/null +++ b/addons/rose/addon/templates/components/rose/form/checkbox.hbs @@ -0,0 +1,16 @@ +
+ + + + + + {{yield}} + +
diff --git a/addons/rose/app/components/rose/checkbox.js b/addons/rose/app/components/rose/checkbox.js deleted file mode 100644 index d3323b60d..000000000 --- a/addons/rose/app/components/rose/checkbox.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'rose/components/rose/checkbox'; diff --git a/addons/rose/app/components/rose/form/checkbox.js b/addons/rose/app/components/rose/form/checkbox.js new file mode 100644 index 000000000..fecc9b373 --- /dev/null +++ b/addons/rose/app/components/rose/form/checkbox.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/form/checkbox'; diff --git a/addons/rose/app/templates/index.hbs b/addons/rose/app/templates/index.hbs index 4e4192bca..c68de7675 100644 --- a/addons/rose/app/templates/index.hbs +++ b/addons/rose/app/templates/index.hbs @@ -64,8 +64,28 @@ />

Checkbox

-Label -Error -Checked -Disabled -Disabled + Checked + + + + + diff --git a/addons/rose/stories/checkbox.stories.js b/addons/rose/stories/form/checkbox.stories.js similarity index 56% rename from addons/rose/stories/checkbox.stories.js rename to addons/rose/stories/form/checkbox.stories.js index 32a978225..2292dd816 100644 --- a/addons/rose/stories/checkbox.stories.js +++ b/addons/rose/stories/form/checkbox.stories.js @@ -2,29 +2,26 @@ import { hbs } from 'ember-cli-htmlbars'; import { text, boolean } from '@storybook/addon-knobs'; export default { - title: 'Rose::Checkbox', + title: 'Rose::Form::Checkbox', }; export const Normal = () => ({ template: hbs` - - {{label}} - + disabled={{disabled}} + /> `, context: { label: text('text', 'Checkbox 🛎'), - id: text('text', 'Identifier'), - name: text('text', 'name'), - form: text('text', 'form'), + id: text('id', 'checkbox-1'), + name: text('name', 'checkbox-1'), checked: boolean('checked', false), + error: boolean('error', false), disabled: boolean('disabled', false), - error: boolean('error', false) }, }); diff --git a/addons/rose/tests/integration/components/rose/checkbox-test.js b/addons/rose/tests/integration/components/rose/checkbox-test.js deleted file mode 100644 index 44b5b895c..000000000 --- a/addons/rose/tests/integration/components/rose/checkbox-test.js +++ /dev/null @@ -1,71 +0,0 @@ -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; -import hbs from 'htmlbars-inline-precompile'; - -module('Integration | Component | rose/checkbox', function (hooks) { - setupRenderingTest(hooks); - - test('it renders without label', async function (assert) { - await render(hbs``); - assert.equal(this.element.textContent.trim(), ''); - }); - - test('it renders a checkbox', async function (assert) { - await render(hbs``); - assert.equal(this.element.querySelector('input').type, 'checkbox'); - }); - - test('it renders with label', async function (assert) { - await render(hbs`Label`); - assert.equal(this.element.textContent.trim(), 'Label'); - }); - - test('it renders in a div wrapper', async function (assert) { - await render(hbs``); - assert.ok(this.element.querySelector('div.rose-checkbox')); - }); - - test('it renders a checkbox class', async function (assert) { - await render(hbs``); - assert.ok(this.element.querySelector('input.rose-checkbox-input')); - }); - - test('it renders a label class', async function (assert) { - await render(hbs``); - assert.ok(this.element.querySelector('label.rose-checkbox-label')); - }); - - test('it is not checked', async function (assert) { - await render(hbs``); - assert.equal(this.element.querySelector('input').checked, false); - }); - - test('it is not disabled', async function (assert) { - await render(hbs``); - assert.equal(this.element.querySelector('input').disabled, false); - }); - - test('it is checked when @checked={{true}}', async function (assert) { - await render(hbs``); - assert.equal(this.element.querySelector('input').checked, true); - }); - - test('it marks error when @error={{true}}', async function (assert) { - await render(hbs``); - assert.ok(this.element.querySelector('div.error')); - }); - - test('it is disabled when @disabled={{true}}', async function (assert) { - await render(hbs``); - assert.equal(this.element.querySelector('input').disabled, true); - }); - - test('it is disabled and checked when @disabled={{true}} and @checked={{true}}', async function (assert) { - await render( - hbs`` - ); - assert.equal(this.element.querySelector('input').checked, true); - assert.equal(this.element.querySelector('input').disabled, true); - }); -}); diff --git a/addons/rose/tests/integration/components/rose/form/checkbox-test.js b/addons/rose/tests/integration/components/rose/form/checkbox-test.js new file mode 100644 index 000000000..e341dd0db --- /dev/null +++ b/addons/rose/tests/integration/components/rose/form/checkbox-test.js @@ -0,0 +1,39 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, find } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +module('Integration | Component | rose/form/checkbox', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + await render(hbs``); + assert.equal(await find('label').textContent.trim(), 'Label'); + assert.ok(await find('input')); + }); + + test('it is not checked by default', async function (assert) { + await render(hbs``); + assert.equal(await find('input').checked, false); + }); + + test('it is not disabled by default', async function (assert) { + await render(hbs``); + assert.equal(await find('input').disabled, false); + }); + + test('it is checked when @checked={{true}}', async function (assert) { + await render(hbs``); + assert.equal(await find('input').checked, true); + }); + + test('it marks error when @error={{true}}', async function (assert) { + await render(hbs``); + assert.ok(await find('.error')); + }); + + test('it is disabled when disabled={{true}}', async function (assert) { + await render(hbs``); + assert.equal(await find('input').disabled, true); + }); +});