Skip to content

Commit

Permalink
feat(input): add text-color props to SInputText and SInputNumber (
Browse files Browse the repository at this point in the history
#218)

Co-authored-by: Kia Ishii <kia.king.08@gmail.com>
  • Loading branch information
ryo-gk and kiaking committed Feb 3, 2023
1 parent a482eee commit e03f642
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 10 deletions.
21 changes: 21 additions & 0 deletions docs/components/input-number.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,27 @@ type Color =
/>
```
### `:text-color`
Defines the color of the input text. You can pass the `TextColor` or the callback that takes an input value as an argument and returns the `TextColor`. The default is `neutral`.
```ts
interface Props {
textColor?: TextColor | ((value: number | null) => TextColor)
}

type TextColor =
| 'neutral'
| 'info'
| 'success'
| 'warning'
| 'danger'
```
```vue-html
<SInputNumber text-color="info" v-model="..." />
```
### `:align`
Defines how the input value is aligned inside the input box. The default is `left`.
Expand Down
21 changes: 18 additions & 3 deletions lib/components/SInputNumber.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import { IconifyIcon } from '@iconify/vue/dist/offline'
import { DefineComponent, computed } from 'vue'
import { Validatable } from '../composables/Validation'
import { isNullish } from '../support/Utils'
import { isNullish, isString } from '../support/Utils'
import SInputText from './SInputText.vue'
export type Size = 'mini' | 'small' | 'medium'
export type Align = 'left' | 'center' | 'right'
export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
export type CheckColor = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
export type TextColor = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
const props = defineProps<{
size?: Size
Expand All @@ -21,7 +22,8 @@ const props = defineProps<{
unitAfter?: any
checkIcon?: IconifyIcon | DefineComponent
checkText?: string
checkColor?: Color
checkColor?: CheckColor
textColor?: TextColor | ((value: number | null) => TextColor)
align?: Align
separator?: boolean
disabled?: boolean
Expand All @@ -43,6 +45,18 @@ const _value = computed(() => {
: props.value !== undefined ? props.value : null
})
const _textColor = computed(() => {
if (!props.textColor) {
return 'neutral'
}
if (isString(props.textColor)) {
return props.textColor
}
return props.textColor(_value.value)
})
const valueWithSeparator = computed(() => {
if (isNullish(_value.value)) {
return null
Expand Down Expand Up @@ -86,6 +100,7 @@ function emitUpdate(value: string | null) {
:check-icon="checkIcon"
:check-text="checkText"
:check-color="checkColor"
:text-color="_textColor"
:align="align"
:disabled="disabled"
:hide-error="hideError"
Expand Down
34 changes: 29 additions & 5 deletions lib/components/SInputText.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import SInputBase from './SInputBase.vue'
export type Size = 'mini' | 'small' | 'medium'
export type Align = 'left' | 'center' | 'right'
export type Color = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
export type CheckColor = 'neutral' | 'mute' | 'info' | 'success' | 'warning' | 'danger'
export type TextColor = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
const props = defineProps<{
size?: Size
Expand All @@ -23,7 +24,8 @@ const props = defineProps<{
unitAfter?: any
checkIcon?: IconifyIcon | DefineComponent
checkText?: string
checkColor?: Color
checkColor?: CheckColor
textColor?: TextColor | ((value: string | null) => TextColor)
align?: Align
disabled?: boolean
modelValue: string | null
Expand All @@ -48,6 +50,23 @@ const classes = computed(() => [
{ disabled: props.disabled }
])
const inputClasses = computed(() => [
textColor.value,
{ hide: showDisplay.value }
])
const textColor = computed(() => {
if (!props.textColor) {
return 'neutral'
}
if (isString(props.textColor)) {
return props.textColor
}
return props.textColor(props.modelValue)
})
const showDisplay = computed(() => {
return !isFocused.value && props.displayValue
})
Expand Down Expand Up @@ -122,7 +141,7 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
<div class="area">
<input
class="input entity"
:class="{ hide: showDisplay }"
:class="inputClasses"
:id="name"
:type="type ?? 'text'"
:placeholder="placeholder"
Expand All @@ -134,7 +153,7 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
@input="emitInput"
@keypress.enter="emitEnter"
>
<div v-if="showDisplay" class="input display">
<div v-if="showDisplay" class="input display" :class="[textColor]">
{{ displayValue }}
</div>
</div>
Expand Down Expand Up @@ -371,10 +390,15 @@ function getValue(e: Event | FocusEvent | KeyboardEvent): string | null {
bottom: 0;
left: 0;
width: 100%;
color: var(--input-value-color);
background-color: transparent;
cursor: text;
&.neutral:not(.hide) { color: var(--input-value-color); }
&.info:not(.hide) { color: var(--c-info-text); }
&.success:not(.hide) { color: var(--c-success-text); }
&.warning:not(.hide) { color: var(--c-warning-text); }
&.danger:not(.hide) { color: var(--c-danger-text); }
&.hide,
&.hide::placeholder {
color: transparent;
Expand Down
13 changes: 13 additions & 0 deletions stories/components/SInputNumber.01_Playground.story.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function state() {
unitBefore: '',
unitAfter: '',
help: 'This is a help text.',
textColor: 'neutral',
align: 'left',
separator: true,
disabled: false,
Expand Down Expand Up @@ -72,6 +73,17 @@ function onInput(value: number | null) {
title="help"
v-model="state.help"
/>
<HstSelect
title="text-color"
:options="{
neutral: 'neutral',
info: 'info',
success: 'success',
warning: 'warning',
danger: 'danger'
}"
v-model="state.textColor"
/>
<HstSelect
title="align"
:options="{
Expand Down Expand Up @@ -106,6 +118,7 @@ function onInput(value: number | null) {
:placeholder="state.placeholder"
:unit-before="state.unitBefore"
:unit-after="state.unitAfter"
:text-color="state.textColor"
:align="state.align"
:separator="state.separator"
:disabled="state.disabled"
Expand Down
17 changes: 15 additions & 2 deletions stories/components/SInputText.01_Playground.story.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ function state() {
unitBefore: '',
unitAfter: '',
help: 'Please fill in your name.',
textColor: 'neutral',
align: 'left',
separator: true,
disabled: false,
Expand Down Expand Up @@ -122,6 +123,17 @@ function state() {
title="help"
v-model="state.help"
/>
<HstSelect
title="text-color"
:options="{
neutral: 'neutral',
info: 'info',
success: 'success',
warning: 'warning',
danger: 'danger'
}"
v-model="state.textColor"
/>
<HstSelect
title="align"
:options="{
Expand All @@ -147,6 +159,7 @@ function state() {

<template #default="{ state }">
<SInputText
:size="state.size"
name="name"
:label="state.label"
:info="state.info"
Expand All @@ -155,12 +168,12 @@ function state() {
:placeholder="state.placeholder"
:unit-before="state.unitBefore"
:unit-after="state.unitAfter"
:size="state.size"
:check-icon="check?.icon"
:check-text="check?.text"
:check-color="check?.color"
:validation="validation.name"
:text-color="state.textColor"
v-model="data.name"
:validation="validation.name"
/>
<div class="actions">
Expand Down
28 changes: 28 additions & 0 deletions tests/components/SInputNumber.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,34 @@ describe('components/SInputNumber', () => {
expect(getInputValue(wrapper.find('.SInputNumber .input'))).toBe('')
})

test('it should apply color when `textColor` prop is set as value', async () => {
const wrapper = mount(SInputNumber, {
props: {
textColor: 'info',
modelValue: 123
}
})

expect(wrapper.find('.SInputNumber .input').classes()).toContain('info')
})

test('it should apply color when `textColor` prop is set as callback', async () => {
const wrapper = mount(SInputNumber, {
props: {
textColor: (value: number | null) => value === 123 ? 'success' : 'danger',
modelValue: 123
}
})

const input = wrapper.find('.SInputNumber .input')

expect(input.classes()).toContain('success')

await wrapper.setProps({ modelValue: 456 })

expect(input.classes()).toContain('danger')
})

test('it emits value on input event', async () => {
const wrapper = mount(SInputNumber, {
props: { modelValue: 1 }
Expand Down
28 changes: 28 additions & 0 deletions tests/components/SInputText.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@ import SInputText from 'sefirot/components/SInputText.vue'
import { assertEmitted } from 'tests/Utils'

describe('components/SInputText', () => {
it('should apply color when `textColor` prop is set as value', async () => {
const wrapper = mount(SInputText, {
props: {
textColor: 'info',
modelValue: 'text'
}
})

expect(wrapper.find('.SInputText .input').classes()).toContain('info')
})

it('should apply color when `textColor` prop is set as callback', async () => {
const wrapper = mount(SInputText, {
props: {
textColor: (value: string | null) => value === 'text' ? 'success' : 'danger',
modelValue: 'text'
}
})

const input = wrapper.find('.SInputText .input')

expect(input.classes()).toContain('success')

await wrapper.setProps({ modelValue: 'not text' })

expect(input.classes()).toContain('danger')
})

it('should emit input event', async () => {
const wrapper = mount(SInputText, {
propsData: {
Expand Down

0 comments on commit e03f642

Please sign in to comment.