chore: 🤖 build out input/text field component

This commit is contained in:
Randall Morey committed 2020-05-04 12:28:43 -04:00
1 parent c8312b9610
commit 9f2b31a86e
13 files changed
+290 -77

No files matched your search

@@ -0,0 +1,19 @@
import Component from '@ember/component';
import layout from '../../../templates/components/rose/form/input';
/**
* A simple HTML `<button>` element.
*
* @example
* <Rose::Form::Input
* @value="Text"
* @label="Label"
* @helperText="Helper text"
* @error={{true}}
* disabled={{true}}
* readonly={{true}}
* />
*/
export default Component.extend({
layout,
});
@@ -2,10 +2,10 @@
@use '../utilities/type' as typography;
@use '../variables/sizing';
$size-xs: sizing.rems(xs); // 8
$size-xxs: sizing.rems(xxs); // 4
$size-xxxs: sizing.rems(xxxs); // 2
$size-xxxxs: sizing.rems(xxxxs); // 1
$size-xs: sizing.rems(xs); // 8
$size-xxs: sizing.rems(xxs); // 4
$size-xxxs: sizing.rems(xxxs); // 2
$size-xxxxs: sizing.rems(xxxxs); // 1
@include button.button-style(
'primary',
@@ -13,8 +13,10 @@ $size-xxxxs: sizing.rems(xxxxs); // 1
color: var(--white),
background-color: var(--info),
border-color: var(--info-starker-2),
box-shadow: 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-2)),
text-shadow: 0 $size-xxxxs $size-xxxxs rgba(var(--black-components), var(--opacity-2)),
box-shadow: 0 $size-xxs $size-xxs
rgba(var(--stark-components), var(--opacity-2)),
text-shadow: 0 $size-xxxxs $size-xxxxs
rgba(var(--black-components), var(--opacity-2)),
),
(
background-color: var(--info-subtler-1),
@@ -39,7 +41,8 @@ $size-xxxxs: sizing.rems(xxxxs); // 1
color: var(--ui-gray-starker-3),
background-color: var(--ui-gray-subtler-5),
border-color: var(--ui-gray-subtler-2),
box-shadow: 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-2)),
box-shadow: 0 $size-xxs $size-xxs
rgba(var(--stark-components), var(--opacity-2)),
),
(
color: var(--ui-gray-starker-4),
@@ -68,7 +71,8 @@ $size-xxxxs: sizing.rems(xxxxs); // 1
color: var(--failure),
background-color: var(--subtle),
border-color: var(--failure),
box-shadow: 0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-2)),
box-shadow: 0 $size-xxs $size-xxs
rgba(var(--stark-components), var(--opacity-2)),
),
(
color: var(--white),
@@ -78,7 +82,8 @@ $size-xxxxs: sizing.rems(xxxxs); // 1
0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-3)),
0 $size-xxs $size-xs rgba(var(--stark-components), var(--opacity-4)),
),
text-shadow: 0 $size-xxxxs $size-xxxxs rgba(var(--black-components), var(--opacity-2)),
text-shadow: 0 $size-xxxxs $size-xxxxs
rgba(var(--black-components), var(--opacity-2)),
),
(
color: var(--white),
@@ -88,7 +93,8 @@ $size-xxxxs: sizing.rems(xxxxs); // 1
0 $size-xxs $size-xxs rgba(var(--stark-components), var(--opacity-3)),
0 $size-xxs $size-xs rgba(var(--stark-components), var(--opacity-4)),
),
text-shadow: 0 $size-xxxxs $size-xxxxs rgba(var(--black-components), var(--opacity-2)),
text-shadow: 0 $size-xxxxs $size-xxxxs
rgba(var(--black-components), var(--opacity-2)),
),
(
border-color: var(--ui-gray-subtler-2),
@@ -1 +1,2 @@
@import 'button';
@import 'form';
@@ -0,0 +1 @@
@import 'input';
@@ -0,0 +1,63 @@
@use '../../variables/sizing';
@use '../../utilities/type';
.rose-form-input {
margin-bottom: sizing.rems(m);
.rose-form-input-label {
@include type.type(s, semibold);
display: block;
padding-bottom: sizing.rems(xxs);
}
.rose-form-input-helper-text {
@include type.type(xs);
display: block;
color: var(--ui-gray);
padding-bottom: sizing.rems(xxs);
}
.rose-form-input-field {
@include type.type(m);
padding: sizing.rems(xs);
color: var(--stark);
background-color: var(--subtle);
border: sizing.rems(xxxxs) solid var(--ui-gray-subtler-2);
border-radius: sizing.rems(xxxs);
box-shadow: inset 0px 3px 1px
rgba(var(--stark-components), var(--opacity-4));
&:not(:disabled):not([readonly]) {
&:hover {
border-color: var(--ui-gray);
}
&:focus {
border-color: var(--info);
}
}
&:disabled {
color: var(--ui-gray);
background-color: var(--ui-gray-subtler-6);
}
&[readonly] {
border-color: transparent;
box-shadow: none;
}
}
&.error {
color: var(--failure);
.rose-form-input-helper-text {
color: currentColor;
}
.rose-form-input-field {
color: var(--failure);
border-color: currentColor;
}
}
}
@@ -5,16 +5,25 @@
*/
$size-coefficients: (
xxl: 3, // 48
xl: 2.25, // 36
l: 1.5, // 24
m: 1, // 16 - base
s: 0.75, // 12
xs: 0.5, // 8
off-xs:0.3125, // 5
xxs: 0.25, // 4
xxxs: 0.125, // 2
xxxxs: 0.0625 // 1
xxl: 3,
// 48
xl: 2.25,
// 36
l: 1.5,
// 24
m: 1,
// 16 - base
s: 0.75,
// 12
xs: 0.5,
// 8
off-xs: 0.3125,
// 5
xxs: 0.25,
// 4
xxxs: 0.125,
// 2
xxxxs: 0.0625 // 1,,
);
/**
@@ -5,33 +5,18 @@
* SASS variables defining typography.
*/
$family-primary:
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
'Roboto',
'Oxygen',
'Ubuntu',
'Cantarell',
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
$family-primary: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
$family-monospace:
'SFMono-Regular',
Consolas,
'Liberation Mono',
Menlo,
Courier,
$family-monospace: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier,
monospace;
// Available font weights by name
$font-weights: (
normal: 400,
semibold: 600,
bold: 700
bold: 700,
);
// unitless value to start, but think pixels or points
@@ -39,17 +24,23 @@ $base-font-size: 16;
// Font sizes don't quite match up with the size scale, so they're recalculated.
$font-sizes: (
xl: sizing.size(m) * 1.75, // 28
l: sizing.size(m) * 1.25, // 20
m: sizing.size(m), // 16
s: sizing.size(m) * 0.875, // 14
xs: sizing.size(m) * 0.75, // 12
h1: sizing.size(m) * 1.75, // 28
h2: sizing.size(m) * 1.25, // 20
h3: sizing.size(m), // 16
button: sizing.size(m) * 0.8125 // 13
xl: sizing.size(m) * 1.75,
// 28
l: sizing.size(m) * 1.25,
// 20
m: sizing.size(m),
// 16
s: sizing.size(m) * 0.875,
// 14
xs: sizing.size(m) * 0.75,
// 12
h1: sizing.size(m) * 1.75,
// 28
h2: sizing.size(m) * 1.25,
// 20
h3: sizing.size(m),
// 16
button: sizing.size(m) * 0.8125 // 13,,
);
// Line height expressed as ratio.
@@ -59,12 +50,10 @@ $line-heights: (
m: 20 / 16,
s: 18 / 14,
xs: 16 / 12,
h1: 48 / 28,
h2: 36 / 20,
h3: 24 / 16,
button: 24 / 13
button: 24 / 13,
);
/**
@@ -0,0 +1,19 @@
<div class="rose-form-input{{if @error " error"}}">
<label class="rose-form-input-label">
{{@label}}
</label>
{{#if @helperText}}
<span class="rose-form-input-helper-text">{{@helperText}}</span>
{{/if}}
<Input
...attributes
class="rose-form-input-field"
@type={{@type}}
@value={{@value}} />
{{yield}}
</div>
@@ -0,0 +1 @@
export { default } from 'rose/components/rose/form/input';
+27
View File
@@ -9,3 +9,30 @@ Dummy app index template
<br>
<Rose::Button @style="secondary" @disabled={{true}}>Secondary</Rose::Button>
<br>
<Rose::Form::Input
@label="Label"
@value="Read only"
readonly={{true}}
/>
<Rose::Form::Input
@label="Label"
@helperText="Helper text"
@value="Text"
/>
<Rose::Form::Input
@label="Label"
@helperText="Helper text"
@value="Text"
@error={{true}}
/>
<Rose::Form::Input
@label="Label"
@helperText="Helper text"
@value="Text"
disabled={{true}}
/>
+27
View File
@@ -0,0 +1,27 @@
import { hbs } from 'ember-cli-htmlbars';
import { text, boolean } from '@storybook/addon-knobs';
export default {
title: 'Rose::Form::Input',
};
export const Normal = () => ({
template: hbs`
<Rose::Form::Input
@value={{value}}
@label={{label}}
@helperText={{helperText}}
@error={{error}}
disabled={{disabled}}
readonly={{read-only}}
/>
`,
context: {
value: text('value', 'Value'),
label: text('label', 'Label'),
helperText: text('helperText', 'Helper text'),
disabled: boolean('disabled', false),
'read-only': boolean('readonly', false),
error: boolean('error', false),
},
});
+27 -23
View File
@@ -326,18 +326,18 @@ border: sizing.rems(xxxxs) solid;</pre>
`,
context: {
sizes: [
{code: 'xxl', pixels: 48},
{code: 'xl', pixels: 36},
{code: 'l', pixels: 24},
{code: 'm', pixels: 16, isBase: true},
{code: 's', pixels: 12},
{code: 'xs', pixels: 8},
{code: 'off-xs', pixels: 5},
{code: 'xxs', pixels: 4},
{code: 'xxxs', pixels: 2},
{code: 'xxxxs', pixels: 1},
]
}
{ code: 'xxl', pixels: 48 },
{ code: 'xl', pixels: 36 },
{ code: 'l', pixels: 24 },
{ code: 'm', pixels: 16, isBase: true },
{ code: 's', pixels: 12 },
{ code: 'xs', pixels: 8 },
{ code: 'off-xs', pixels: 5 },
{ code: 'xxs', pixels: 4 },
{ code: 'xxxs', pixels: 2 },
{ code: 'xxxxs', pixels: 1 },
],
},
});
export const Typography = () => ({
@@ -424,15 +424,19 @@ export const Typography = () => ({
`,
context: {
sizes: [
{code: 'xl', pixels: 28},
{code: 'l', pixels: 20},
{code: 'm', pixels: 16},
{code: 's', pixels: 14},
{code: 'xs', pixels: 12},
{code: 'h1', pixels: 28, note: 'Same as xl, but different line height.'},
{code: 'h2', pixels: 20, note: 'Same as l, but different line height.'},
{code: 'h3', pixels: 16, note: 'Same as m, but different line height.'},
{code: 'button', pixels: 13}
]
}
{ code: 'xl', pixels: 28 },
{ code: 'l', pixels: 20 },
{ code: 'm', pixels: 16 },
{ code: 's', pixels: 14 },
{ code: 'xs', pixels: 12 },
{
code: 'h1',
pixels: 28,
note: 'Same as xl, but different line height.',
},
{ code: 'h2', pixels: 20, note: 'Same as l, but different line height.' },
{ code: 'h3', pixels: 16, note: 'Same as m, but different line height.' },
{ code: 'button', pixels: 13 },
],
},
});
@@ -0,0 +1,47 @@
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/input', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
await render(hbs`<Rose::Form::Input @value="Text" @label="Label" />`);
assert.equal(await find('label').textContent.trim(), 'Label');
assert.equal(await find('input').value, 'Text');
});
test('it displays optional helper text', async function (assert) {
await render(hbs`<Rose::Form::Input @helperText="Help me" />`);
assert.equal(
await find('.rose-form-input-helper-text').textContent.trim(),
'Help me'
);
});
test('it is type="text" by default', async function (assert) {
await render(hbs`<Rose::Form::Input />`);
assert.equal(await find('input').type, 'text');
});
test('it supports a @type attribute', async function (assert) {
await render(hbs`<Rose::Form::Input @type="password" />`);
assert.equal(await find('input').type, 'password');
});
test('it supports disabled attribute', async function (assert) {
await render(hbs`<Rose::Form::Input disabled={{true}} />`);
assert.ok(await find('[disabled]'));
});
test('it supports readonly attribute', async function (assert) {
await render(hbs`<Rose::Form::Input readonly={{true}} />`);
assert.ok(await find('[readonly]'));
});
test('it gets an `error` class when @error=true', async function (assert) {
await render(hbs`<Rose::Form::Input @error={{true}} />`);
assert.ok(await find('.error'));
});
});