Skip to content

Commit

Permalink
Allow elements to be ridiculously customizable
Browse files Browse the repository at this point in the history
Split the input element into separate input and label elements, and export a
new type called labeledInput to be more truthful to what you're getting.

Allow classes and styles to be added (open q: should we allow removing current?,
and if so, how?), and allow arbitrary attributes to be set on the elements
allowing for custom behaviors.
  • Loading branch information
toddself authored and lrlna committed Jun 14, 2017
1 parent a27fc7a commit 7598bd8
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 21 deletions.
30 changes: 27 additions & 3 deletions button-el.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
const html = require('bel')
const noop = () => {}

function button (text, handler) {
return html`<button class="w-100 mt3 pt1 pb2 lh-copy outline-0 bn br2" style="background: #00C9D8;" onclick=${handler}>
<span class="b f5 white">${text}</span>
function button (text, handler, buttonOpts) {
handler = typeof handler === 'function' ? handler : noop
buttonOpts = buttonOpts || {}

const classes = ['b', 'f5', 'white', 'w-100', 'mt3', 'pt1', 'pb2', 'lh-copy', 'outline-0', 'bn', 'br2']
let style = 'background: #00C9D8;'

if (Array.isArray(buttonOpts.classes)) {
classes.push.apply(classes, buttonOpts.classes)
}

if (typeof buttonOpts.style === 'string') {
style += buttonOpts.style
}

const $button = html`<button class="${classes.join(' ')}" style="${style}" onclick=${handler}>
${text}
</button>`

Object
.keys(buttonOpts)
.filter((key) => key !== 'classes' || key !== 'style')
.forEach((key) => {
$button[key] = buttonOpts[key]
})

return $button
}

module.exports = button
2 changes: 2 additions & 0 deletions form-elements.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
exports.input = require('./input-el')
exports.button = require('./button-el')
exports.label = require('./label-el')
exports.labeledInput = require('./labeled-input-el')
33 changes: 29 additions & 4 deletions input-el.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
const html = require('bel')
const noop = () => {}

function input (label, type, handler, value) {
return html`<label class="db mt3" style="text-indent: 4px;"><span class="ttu lh-copy" style="letter-spacing: 0.01em; font-size: 0.625rem">${label}</span><br>
<input class="w-100 outline-0 ba b--moon-gray f6 pa1 br2" type="${type}" oninput=${handler} value="${value}">
</label>`
function input (type, handler, value, inputOpts) {
handler = typeof handler === 'function' ? handler : noop
inputOpts = inputOpts || {}

const classes = ['w-100', 'outline-0', 'ba', 'b--moon-gray', 'f6', 'pa1', 'br2']
let style = ''

if (Array.isArray(inputOpts.classes)) {
classes.push.apply(classes, inputOpts.classes)
}

if (typeof inputOpts.style === 'string') {
style += inputOpts.style
}

const $inputEl = html`<input class="${classes.join(' ')}" style="${style}"
type="${type}"
oninput=${handler}
value="${value}">`

Object
.keys(inputOpts)
.filter((key) => key !== 'classes' || key !== 'style')
.forEach((key) => {
$inputEl[key] = inputOpts[key]
})

return $inputEl
}

module.exports = input
30 changes: 30 additions & 0 deletions label-el.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const html = require('bel')

function label (label, labelOpts) {
labelOpts = labelOpts || {}
const classes = ['db', 'mt3', 'ttu', 'lh-copy']
let style = 'text-indent: 4px;'

if (Array.isArray(labelOpts.classes)) {
classes.push.apply(classes, labelOpts.classes)
}

if (typeof labelOpts.style === 'string') {
style += labelOpts.style
}

const $label = html`<label class="${classes.join(' ')}" style="${style}">
${label}<br>
</label>`

Object
.keys(labelOpts)
.filter((key) => key !== 'classes' || key !== 'style')
.forEach((key) => {
$label[key] = labelOpts[key]
})

return $label
}

module.exports = label
11 changes: 11 additions & 0 deletions labeled-input-el.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const inputEl = require('./input-el')
const labelEl = require('./label-el')

function labeledInput (text, type, handler, value, inputOpts, labelOpts) {
const $label = labelEl(text, labelOpts)
const $input = inputEl(type, handler, value, inputOpts)
$label.appendChild($input)
return $label
}

module.exports = labeledInput
17 changes: 15 additions & 2 deletions standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,27 @@ const scratch = {
}

function main (state, emit) {
const inputOpts = {
classes: ['w-50'],
onblur: function (evt) {
console.log('you blurred me')
}
}

const $input = els.labeledInput('test input', 'text', oninput, scratch.test, inputOpts)

return html`<div class="dt w-third center vh-100">
<div class="v-mid dtc">
<b>Button pushed:</b> ${scratch.pushed}<br>
<b>Input value:</b> ${scratch.text}<br>
${els.input('test input', 'text', (evt) => (scratch.text = evt.currentTarget.value), scratch.test)}<br>
<b>Input value:</b> ${scratch.test}<br>
${$input}<br>
${els.button('i am a button', (evt) => ++scratch.pushed && emit('render'))}
</div>
</div>`

function oninput (evt) {
scratch.test = evt.currentTarget.value
}
}

entry([], [{route: '/', method: main}], 'body')
75 changes: 63 additions & 12 deletions test/form-elments.spec.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,83 @@
const test = require('tape')
const els = require('../')

test('input', (t) => {
test('input, no options', (t) => {
t.plan(2)
let $inputValue = ''
const $label = els.input('input test', 'text', handleInput, $inputValue)
const $input = $label.querySelector('input')
const $input = els.input('text', handleInput, $inputValue)
$input.value = 'test'
$input.dispatchEvent(new window.Event('input'))

setTimeout(() => {
t.equal($inputValue, 'test', 'updated')
t.equal($label.innerText.trim(), 'input test', 'label')
t.equal($input.type, 'text', 'text input')
t.end()
}, 1)
t.equal($inputValue, 'test', 'updated')
t.equal($input.type, 'text', 'text input')

function handleInput (evt) {
$inputValue = evt.currentTarget.value
}
})

test('button', (t) => {
test('input, options', (t) => {
t.plan(3)

const opts = {
classes: ['foo'],
style: 'display: inline-block;',
onblur: (evt) => t.pass('blurrrrrrrr')
}

const $input = els.input('text', null, '', opts)
t.ok($input.classList.contains('foo'), 'has foo class')
t.equal($input.getAttribute('style'), opts.style, 'has style')
$input.dispatchEvent(new window.Event('blur'))
})

test('label, no options', (t) => {
t.plan(1)
const $label = els.label('yo')
t.ok($label.innerText.trim(), 'yo', 'labeled')
})

test('label, options', (t) => {
t.plan(3)
const opts = {
classes: ['foo'],
style: 'border: 1px;',
for: 'boop'
}
const $label = els.label('yo', opts)
t.ok($label.classList.contains('foo'), 'has foo')
t.ok($label.getAttribute('style').includes(opts.style), 'has style')
t.equal($label.for, opts.for, 'for booping')
})

test('labeled input', (t) => {
t.plan(3)
const $labeledInput = els.labeledInput('test', 'text', null, '')
const $input = $labeledInput.querySelectorAll('input')
t.equal($labeledInput.nodeName, 'LABEL', 'label is outer')
t.equal($input.length, 1, 'has an input')
t.equal($input[0].type, 'text', 'is a text input')
})

test('button, no options', (t) => {
t.plan(2)
const $button = els.button('push me', handleClick)
$button.click()
t.equal($button.innerText.trim(), 'push me', 'has text')

function handleClick () {
t.pass('clicked')
t.end()
}
})

test('button, options', (t) => {
t.plan(3)
const opts = {
classes: ['baz'],
style: 'border: 1px;',
disabled: 'disabled'
}
const $button = els.button('push me', null, opts)
t.ok($button.classList.contains('baz'), 'has baz')
t.ok($button.getAttribute('style').includes(opts.style), 'has style')
t.ok($button.disabled, 'disabled')
})

0 comments on commit 7598bd8

Please sign in to comment.