diff --git a/addons/rose/addon/components/rose/form/radio/card/index.js b/addons/rose/addon/components/rose/form/radio/card/index.js new file mode 100644 index 000000000..c05cf1a5b --- /dev/null +++ b/addons/rose/addon/components/rose/form/radio/card/index.js @@ -0,0 +1,7 @@ +import RoseFormRadioRadioComponent from '../radio'; + +export default class RoseFormRadioCardComponent extends RoseFormRadioRadioComponent { + // =attributes + layout = this.args.layout; + className = `rose-form-radio-card ${this.layout}`; +} diff --git a/addons/rose/addon/components/rose/form/radio/card/index.stories.mdx b/addons/rose/addon/components/rose/form/radio/card/index.stories.mdx new file mode 100644 index 000000000..360108563 --- /dev/null +++ b/addons/rose/addon/components/rose/form/radio/card/index.stories.mdx @@ -0,0 +1,100 @@ +import { Meta, Story, Preview } from '@storybook/addon-docs/blocks'; +import { hbs } from 'ember-cli-htmlbars'; +import { text, boolean, select } from '@storybook/addon-knobs'; + + + +# Form Radiofield in a wide format + +Renders a form radiofield in a Wide format. + +```htmlbars + +``` + + + {{ + template: hbs` + + `, + context: { + label: text('text', 'Azure'), + error: boolean('error', false), + value: text('value', 'value and selectedValue must match to select radiofield'), + selectedValue: text( + 'selectedValue', + 'value and selectedValue must match to select radiofield' + ), + helperText: text('helperText', 'Record financial transactions using'), + disabled: boolean('disabled', false), + } + }} + + + +Renders a form radiofield in a tile format. + +```htmlbars + +``` + + + {{ + template: hbs` + + `, + context: { + label: text('text', 'Azure'), + error: boolean('error', false), + value: text('value', 'value and selectedValue must match to select radiofield'), + selectedValue: text( + 'selectedValue', + 'value and selectedValue must match to select radiofield' + ), + helperText: text('helperText', 'Record financial transactions using'), + disabled: boolean('disabled', false), + icon: select( + 'icon', + { + 'none': null, + 'flight-icons/svg/azure-color-16': 'flight-icons/svg/azure-color-16', + }, + null + ), + } + }} + diff --git a/addons/rose/addon/components/rose/form/radio/radio/index.hbs b/addons/rose/addon/components/rose/form/radio/radio/index.hbs index edecc3ba8..b2e204478 100644 --- a/addons/rose/addon/components/rose/form/radio/radio/index.hbs +++ b/addons/rose/addon/components/rose/form/radio/radio/index.hbs @@ -22,6 +22,7 @@ {{/if}} {{@label}} + {{#if @helperText}} {{@helperText}} diff --git a/addons/rose/addon/styles/rose/components/form/_index.scss b/addons/rose/addon/styles/rose/components/form/_index.scss index 73137ac8d..b06e535f6 100644 --- a/addons/rose/addon/styles/rose/components/form/_index.scss +++ b/addons/rose/addon/styles/rose/components/form/_index.scss @@ -6,3 +6,4 @@ @import 'select'; @import 'radio'; @import 'fieldset'; +@import 'radio-card'; diff --git a/addons/rose/addon/styles/rose/components/form/_radio-card.scss b/addons/rose/addon/styles/rose/components/form/_radio-card.scss new file mode 100644 index 000000000..85364923a --- /dev/null +++ b/addons/rose/addon/styles/rose/components/form/_radio-card.scss @@ -0,0 +1,215 @@ +@use '../../variables/sizing'; +@use '../../utilities/type'; +@use '../../variables/typography'; + +$size-xxl: sizing.rems(xxl); //48 +$size-xl: sizing.rems(xl); //36 +$size-l: sizing.rems(l); // 24 +$size-m: sizing.rems(m); // 16 +$size-s: sizing.rems(s); //12 +$size-xs: sizing.rems(xs); // 8 +$size-xxs: sizing.rems(xxs); // 4 +$size-xxxs: sizing.rems(xxxs); // 2 +$size-xxxxs: sizing.rems(xxxxs); // 1 + +$radio-svg: ( + 'default': + "", + 'hover': + "", + 'focus': + "", + 'checked': + "", + 'disabled': + "", + 'disabled-checked': + "", + 'error': + "", + 'error-checked': + "", +); + +.rose-form-radio-card { + $cardWideWidth: $size-m * 11; + $cardTileWidth: $size-m * 7.25; + $borderWidth: $size-xxxs * 2; + + &.wide { + width: ($size-m) * 23; + } + + &.tile { + width: ($size-m) * 15; + + .rose-icon { + --size: #{$size-xl}; + padding: $size-xxxs; + } + .rose-form-radio-label { + display: flex; + flex-direction: column; + align-items: center; + height: $size-m * 9; + &::after { + left: $cardTileWidth - $borderWidth; + } + } + } + position: relative; + display: flex; + align-items: center; + justify-content: center; + + .radio-card-overlay { + position: absolute; + height: $size-l + $size-xxs; + background-color: var(--ui-gray-subtler-5); + width: 100%; + bottom: 0; + right: 0; + border-bottom-left-radius: $size-xxxs; + border-bottom-right-radius: $size-xxxs; + } + .rose-form-radio-label { + @include type.type(s, bold); + display: flex; + color: var(--black); + width: ($size-m) * 23; + height: ($size-m) * 7.4; + display: block; + position: relative; + text-align: left; + word-break: break-all; + border: $size-xxxxs solid var(--ui-gray-subtler-6); + box-shadow: 0 $size-xxs $size-xxs + rgba(var(--stark-components), var(--opacity-2)); + border-radius: $size-xxs; + padding: $size-s $size-m; + box-sizing: border-box; + background-color: var(--white); + .rose-form-helper-text { + @include type.type(xs, normal); + } + + &::after { + position: absolute; + bottom: $size-xxs + $size-xxxxs; + right: $cardWideWidth - $borderWidth; + display: block; + width: $size-m; + height: $size-m; + transform: scale(2); + pointer-events: none; + content: ' '; + background: no-repeat 50%/50% 50%; + background-image: url($data-svg-prefix + map-get($radio-svg, 'default')); + } + } + .rose-form-radio-field { + position: absolute; + z-index: 1; + bottom: $size-xs; + opacity: 0; + + &:not(:disabled) { + &:focus { + ~ .rose-form-radio-label { + &::after { + background-image: url($data-svg-prefix + map-get( + $radio-svg, + 'focus' + )); + } + } + } + &:hover { + ~ .rose-form-radio-label { + border: $size-xxxxs solid var(--ui-gray); + } + } + &:checked { + ~ .rose-form-radio-label { + &::after { + background-image: url($data-svg-prefix + map-get( + $radio-svg, + 'checked' + )); + } + .radio-card-overlay { + background-color: var(--action-subtler-1h); + } + border: $size-xxxxs solid var(--info-subtler-1); + } + } + } + + &:disabled { + ~ .rose-form-radio-label { + border: $size-xxxxs solid var(--ui-border-subtler-1); + background-color: var(--ui-gray-subtler-6); + color: var(--ui-gray-starker-1); + &::after { + background-image: url($data-svg-prefix + map-get( + $radio-svg, + 'disabled' + )); + } + } + + &:checked { + ~ .rose-form-radio-label { + &::after { + background-image: url($data-svg-prefix + map-get( + $radio-svg, + 'disabled-checked' + )); + } + } + } + } + } + + &.error { + .rose-form-radio-field { + &:not(:disabled):not(:focus) { + &:not(:disabled):not(:focus) { + ~ .rose-form-radio-label { + color: var(--ui-gray); + border: $size-xxxxs solid var(--failure); + + .radio-card-overlay { + background-color: var(--failure-subtler-2); + } + .rose-form-helper-text.error { + color: var(--ui-gray); + } + &::after { + background-image: url($data-svg-prefix + map-get( + $radio-svg, + 'error' + )); + } + } + + &:checked { + ~ .rose-form-radio-label { + color: var(--ui-gray); + border: $size-xxxxs solid var(--failure); + .radio-card-overlay { + background-color: var(--failure-subtler-2); + } + &::after { + background-image: url($data-svg-prefix + map-get( + $radio-svg, + 'error-checked' + )); + } + } + } + } + } + } + } +} diff --git a/addons/rose/app/components/rose/form/radio/card.js b/addons/rose/app/components/rose/form/radio/card.js new file mode 100644 index 000000000..8a1494dac --- /dev/null +++ b/addons/rose/app/components/rose/form/radio/card.js @@ -0,0 +1 @@ +export { default } from 'rose/components/rose/form/radio/card'; diff --git a/addons/rose/tests/integration/components/rose/form/radio/card-test.js b/addons/rose/tests/integration/components/rose/form/radio/card-test.js new file mode 100644 index 000000000..f3dff2206 --- /dev/null +++ b/addons/rose/tests/integration/components/rose/form/radio/card-test.js @@ -0,0 +1,53 @@ +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/radio/card', function (hooks) { + setupRenderingTest(hooks); + + test('it renders', async function (assert) { + await render(hbs``); + assert.ok(await find('input')); + assert.equal(await find('input').type, 'radio'); + }); + + test('it renders in a tile format when tile layout is applied', async function (assert) { + await render(hbs``); + assert.ok(await find('.tile')); + }); + + test('it supports an icon', async function (assert) { + await render( + hbs`` + ); + assert.ok(find('svg')); + }); + + test('it is checked when @value and @selectedValue values match', async function (assert) { + await render( + hbs`` + ); + assert.true(await find('input').checked); + }); + + test('it is not disabled by default', async function (assert) { + await render(hbs``); + assert.false(await find('input').disabled); + }); + + test('it is not in error state by default', async function (assert) { + await render(hbs``); + assert.notOk(await find('.error')); + }); + + test('it is disabled when @disabled={{true}}', async function (assert) { + await render(hbs``); + assert.true(await find('input').disabled); + }); + + test('it is in error state when @error={{true}}', async function (assert) { + await render(hbs``); + assert.ok(await find('.error')); + }); +});