Skip to content

Commit

Permalink
feat: translate into typescript
Browse files Browse the repository at this point in the history
- use Composition API
- split CronCore into useCronSegment, useCron and useCronComponent
- replace RenderlessSelect with useSelect and useSelectComponent
- removed default exports

BREAKING CHANGES: almost every point listed above
  • Loading branch information
abichinger committed Oct 14, 2023
1 parent d4791d6 commit 7550a1d
Show file tree
Hide file tree
Showing 27 changed files with 1,895 additions and 262 deletions.
2 changes: 2 additions & 0 deletions core-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
"format": "prettier --write src/"
},
"dependencies": {
"mustache": "^4.2.0",
"vue": "^3.3.4"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node18": "^18.2.2",
"@types/jsdom": "^21.1.3",
"@types/mustache": "^4.2.3",
"@types/node": "^18.17.17",
"@vitejs/plugin-vue": "^4.3.4",
"@vue/eslint-config-prettier": "^8.0.0",
Expand Down
101 changes: 67 additions & 34 deletions core-ts/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,47 +1,80 @@
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
</script>

<template>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
<div id="app">
Cron:
<input :value="value" @change="value = ($event.target as HTMLInputElement).value" type="text" />
<CronCore v-model="value" #default="{ period, error, fields }">
<div>
<span>
{{ period.prefix }}:
<select
@input="
period.events['update:model-value'](
JSON.parse(($event.target as HTMLInputElement).value).id
)
"
>
<option v-for="item in period.items" :key="item.text" :value="JSON.stringify(item)">
{{ item.text }}
</option>
</select>
</span>

<template v-for="f in fields" :key="f.id">
<span>
{{ f.prefix }}
<select @input="f.events['update:model-value'](getSelected($event.target))" multiple>
<option v-for="item in f.items" :key="item.value" :value="item.value">
{{ item.text }}
</option>
</select>
{{ f.suffix }}
</span>
</template>

<div class="wrapper">
<HelloWorld msg="You did it!" />
</div>
</header>
<div>-</div>

<main>
<TheWelcome />
</main>
<div>error:{{ error }}</div>
<div>period:{{ period.attrs.value }}</div>
<div v-for="f in fields" :key="'div' + f.id">
{{ f.id }}: {{ f.attrs.value }}, {{ f.cron }}, {{ f.selectedStr }}
</div>
</div>
</CronCore>
</div>
</template>

<style scoped>
header {
line-height: 1.5;
select[multiple] {
height: 200px;
}
</style>

.logo {
display: block;
margin: 0 auto 2rem;
}
<script lang="ts">
import { useCronComponent } from './components/cron-core'
@media (min-width: 1024px) {
header {
display: flex;
place-items: center;
padding-right: calc(var(--section-gap) / 2);
}
export default {
components: {
'CronCore': useCronComponent()
},
props: {},
data() {
return {
value: '* * * * *'
}
},
.logo {
margin: 0 2rem 0 0;
}
computed: {},
watch: {},
header .wrapper {
display: flex;
place-items: flex-start;
flex-wrap: wrap;
methods: {
getSelected(select: any) {
const options = select && select.options
return Array.from(options)
.filter((opt: any) => opt.selected)
.map((opt: any) => opt.value)
}
}
}
</style>
</script>
./components/core
47 changes: 47 additions & 0 deletions core-ts/src/__tests__/cron.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { arrayToSegment, cronToSegment } from '@/cron'
import { FieldWrapper } from '@/types'
import { genItems } from '@/util'
import { describe, expect, it } from 'vitest'

const r = (min: number, max: number) => {
return new FieldWrapper({ id: 'fieldId', items: genItems(min, max) })
}

describe('segments', () => {
it('cronToSegment', () => {
const cronToArray = (cron: string, field: FieldWrapper) => {
return cronToSegment(cron, field)?.toArray() ?? null
}

expect(cronToArray('*', r(1, 3))).toEqual([])
expect(cronToArray('1,3,5', r(0, 24))).toEqual([1, 3, 5])
expect(cronToArray('*/5', r(0, 11))).toEqual([0, 5, 10])
expect(cronToArray('*/5', r(1, 11))).toEqual([5, 10])
expect(cronToArray('10-15', r(0, 59))).toEqual([10, 11, 12, 13, 14, 15])
expect(cronToArray('10-11,20-22,30-33', r(0, 59))).toEqual([10, 11, 20, 21, 22, 30, 31, 32, 33])
expect(cronToArray('5,7-8', r(0, 59))).toEqual([5, 7, 8])

expect(cronToArray('x', r(0, 59))).toBe(null)
expect(cronToArray('1-60', r(0, 59))).toBe(null)
expect(cronToArray('0-10', r(1, 59))).toBe(null)
expect(cronToArray('60', r(0, 59))).toBe(null)
expect(cronToArray('0', r(1, 10))).toBe(null)
expect(cronToArray('*/90', r(1, 10))).toBe(null)
})

it('arrayToSegment', () => {
const arrayToCron = (arr: number[], field: FieldWrapper) => {
return arrayToSegment(arr, field)?.toCron() ?? null
}

expect(arrayToCron([1, 10], r(1, 10))).toEqual('1,10')
expect(arrayToCron([1, 2, 3], r(1, 10))).toEqual('1-3')
expect(arrayToCron([2, 4, 6], r(1, 10))).toEqual('2,4,6')
expect(arrayToCron([], r(1, 3))).toEqual('*')
expect(arrayToCron([1, 2, 3], r(1, 3))).toEqual('*')
expect(arrayToCron([0, 5, 10], r(0, 10))).toEqual('*/5')
expect(arrayToCron([7, 14, 21, 28], r(5, 30))).toEqual('*/7')
expect(arrayToCron([0, 5, 10], r(0, 20))).toEqual('0,5,10')
expect(arrayToCron([1, 2, 5, 8, 9, 10], r(1, 10))).toEqual('1-2,5,8-10')
})
})
73 changes: 73 additions & 0 deletions core-ts/src/__tests__/locale.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { getLocale } from '@/locale'
import { CronType, TextPosition } from '@/types'
import { describe, expect, it } from 'vitest'

describe('locale', () => {
it('getLocale', () => {
const testCases = [
{ locale: 'en', expected: 'Hour' },
{ locale: 'foo-bar', expected: 'Hour' },
{ locale: 'de', expected: 'Stunde' },
{ locale: 'DE-AT', expected: 'Stunde' },
{ locale: 'de-li', expected: 'Stunde' }
]

for (const test of testCases) {
const l = getLocale(test.locale)
expect(l.getLocaleStr('hour', 'text')).toBe(test.expected)
}
})

it('getLocaleStr', () => {
const l = getLocale('en', {
custom: {
'*': 'bar',
message: 'baz'
}
})

expect(l.getLocaleStr('year', 'minute', 'empty', 'text')).toBe('every {{field.id}}')
expect(l.getLocaleStr('year', 'dayOfWeek', 'value', 'prefix')).toBe('and')
expect(l.getLocaleStr('year', 'minute', 'range', 'prefix')).toBe(':')
expect(l.getLocaleStr('custom', 'foo')).toBe('bar')
expect(l.getLocaleStr('custom', 'message')).toBe('baz')
})

it('getLocaleStr pt', () => {
const l = getLocale('pt', {
custom: {
'*': 'bar',
message: 'baz'
}
})

expect(l.getLocaleStr('year', 'minute', 'empty', 'text')).toBe('cada minuto')
expect(l.getLocaleStr('year', 'dayOfWeek', 'value', 'prefix')).toBe('e de')
expect(l.getLocaleStr('year', 'minute', 'range', 'prefix')).toBe(':')
expect(l.getLocaleStr('custom', 'foo')).toBe('bar')
expect(l.getLocaleStr('custom', 'message')).toBe('baz')
})

it('render', () => {
const l = getLocale('en', {
'*': {
'*': {
value: {
text: '{{start.text}}-{{end.text}}'
}
}
}
})

expect(
l.render('period', 'field', CronType.Value, TextPosition.Text, {
start: {
text: 'foo'
},
end: {
text: 'bar'
}
})
).toBe('foo-bar')
})
})
17 changes: 17 additions & 0 deletions core-ts/src/__tests__/util.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Range, deepMerge } from '@/util'
import { describe, expect, it } from 'vitest'

describe('util', () => {
it('Range', () => {
const r = new Range(0, 10, 2)
expect(r[0]).toBe(0)
expect(r[5]).toBe(10)
expect(Array.from(r)).toEqual([0, 2, 4, 6, 8, 10])
})

it('deepMerge', () => {
expect(deepMerge({ a: { a: 1 } }, { a: { b: 1 }, b: 1 })).toEqual({ a: { a: 1, b: 1 }, b: 1 })
expect(deepMerge({}, { a: { b: 1 } })).toEqual({ a: { b: 1 } })
expect(deepMerge({ a: { b: 1 } }, { a: { b: 2 } })).toEqual({ a: { b: 2 } })
})
})
41 changes: 0 additions & 41 deletions core-ts/src/components/HelloWorld.vue

This file was deleted.

Loading

0 comments on commit 7550a1d

Please sign in to comment.