-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(list-converter): a small converter who deals with column based d…
…ata and do some stuff with it (#387) * feat(list-converter): a small converter who deals with column based data and do some stuff with it * Update src/tools/list-converter/index.ts * Update src/tools/list-converter/index.ts * Update src/tools/list-converter/index.ts --------- Co-authored-by: Corentin THOMASSET <[email protected]> fix(list-format): fix e2e
- Loading branch information
1 parent
ce3150c
commit 83a7b3b
Showing
9 changed files
with
441 additions
and
139 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { List } from '@vicons/tabler'; | ||
import { defineTool } from '../tool'; | ||
|
||
export const tool = defineTool({ | ||
name: 'List converter', | ||
path: '/list-converter', | ||
description: | ||
'This tool can process column-based data and apply various changes (transpose, add prefix and suffix, reverse list, sort list, lowercase values, truncate values) to each row.', | ||
keywords: ['list', 'converter', 'sort', 'reverse', 'prefix', 'suffix', 'lowercase', 'truncate'], | ||
component: () => import('./list-converter.vue'), | ||
icon: List, | ||
createdAt: new Date('2023-05-07'), | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { test, expect } from '@playwright/test'; | ||
|
||
test.describe('Tool - List converter', () => { | ||
test.beforeEach(async ({ page }) => { | ||
await page.goto('/list-converter'); | ||
}); | ||
|
||
test('Has correct title', async ({ page }) => { | ||
await expect(page).toHaveTitle('List converter - IT Tools'); | ||
}); | ||
|
||
test('Simple list should be converted with default settings', async ({ page }) => { | ||
await page.getByTestId('input').fill(`1 | ||
2 | ||
3 | ||
4 | ||
5`); | ||
|
||
const result = await page.getByTestId('area-content').innerText(); | ||
|
||
expect(result.trim()).toEqual('1, 2, 3, 4, 5'); | ||
}); | ||
|
||
test('Duplicates should be removed, list should be sorted and prefix and suffix list items', async ({ page }) => { | ||
await page.getByTestId('input').fill(`1 | ||
2 | ||
2 | ||
4 | ||
4 | ||
3 | ||
5`); | ||
await page.getByTestId('removeDuplicates').check(); | ||
await page.getByTestId('itemPrefix').locator('input').fill("'"); | ||
await page.getByTestId('itemSuffix').locator('input').fill("'"); | ||
|
||
const result = await page.getByTestId('area-content').innerText(); | ||
expect(result.trim()).toEqual("'1', '2', '4', '3', '5'"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { expect, describe, it } from 'vitest'; | ||
import { convert } from './list-converter.models'; | ||
import type { ConvertOptions } from './list-converter.types'; | ||
|
||
describe('list-converter', () => { | ||
describe('convert', () => { | ||
it('should convert a given list', () => { | ||
const options: ConvertOptions = { | ||
separator: ', ', | ||
trimItems: true, | ||
removeDuplicates: true, | ||
itemPrefix: '"', | ||
itemSuffix: '"', | ||
listPrefix: '', | ||
listSuffix: '', | ||
reverseList: false, | ||
sortList: null, | ||
lowerCase: false, | ||
keepLineBreaks: false, | ||
}; | ||
const input = ` | ||
1 | ||
2 | ||
3 | ||
3 | ||
4 | ||
`; | ||
expect(convert(input, options)).toEqual('"1", "2", "3", "4"'); | ||
}); | ||
|
||
it('should return an empty value for an empty input', () => { | ||
const options: ConvertOptions = { | ||
separator: ', ', | ||
trimItems: true, | ||
removeDuplicates: true, | ||
itemPrefix: '', | ||
itemSuffix: '', | ||
listPrefix: '', | ||
listSuffix: '', | ||
reverseList: false, | ||
sortList: null, | ||
lowerCase: false, | ||
keepLineBreaks: false, | ||
}; | ||
expect(convert('', options)).toEqual(''); | ||
}); | ||
|
||
it('should keep line breaks', () => { | ||
const options: ConvertOptions = { | ||
separator: '', | ||
trimItems: true, | ||
itemPrefix: '<li>', | ||
itemSuffix: '</li>', | ||
listPrefix: '<ul>', | ||
listSuffix: '</ul>', | ||
keepLineBreaks: true, | ||
lowerCase: false, | ||
removeDuplicates: false, | ||
reverseList: false, | ||
sortList: null, | ||
}; | ||
const input = ` | ||
1 | ||
2 | ||
3 | ||
`; | ||
const expected = `<ul> | ||
<li>1</li> | ||
<li>2</li> | ||
<li>3</li> | ||
</ul>`; | ||
expect(convert(input, options)).toEqual(expected); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import _ from 'lodash'; | ||
import { byOrder } from '@/utils/array'; | ||
import type { ConvertOptions } from './list-converter.types'; | ||
|
||
export { convert }; | ||
|
||
const whenever = | ||
<T, R>(condition: boolean, fn: (value: T) => R) => | ||
(value: T) => | ||
condition ? fn(value) : value; | ||
|
||
function convert(list: string, options: ConvertOptions): string { | ||
const lineBreak = options.keepLineBreaks ? '\n' : ''; | ||
|
||
return _.chain(list) | ||
.thru(whenever(options.lowerCase, (text) => text.toLowerCase())) | ||
.split('\n') | ||
.thru(whenever(options.removeDuplicates, _.uniq)) | ||
.thru(whenever(options.reverseList, _.reverse)) | ||
.thru(whenever(!_.isNull(options.sortList), (parts) => parts.sort(byOrder({ order: options.sortList })))) | ||
.map(whenever(options.trimItems, _.trim)) | ||
.without('') | ||
.map((p) => options.itemPrefix + p + options.itemSuffix) | ||
.join(options.separator + lineBreak) | ||
.thru((text) => [options.listPrefix, text, options.listSuffix].join(lineBreak)) | ||
.value(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export type SortOrder = 'asc' | 'desc' | null; | ||
|
||
export type ConvertOptions = { | ||
lowerCase: boolean; | ||
trimItems: boolean; | ||
itemPrefix: string; | ||
itemSuffix: string; | ||
listPrefix: string; | ||
listSuffix: string; | ||
reverseList: boolean; | ||
sortList: SortOrder; | ||
removeDuplicates: boolean; | ||
separator: string; | ||
keepLineBreaks: boolean; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<template> | ||
<div style="flex: 0 0 100%"> | ||
<n-space item-style="flex: 1 1 0" style="margin: 0 auto; max-width: 600px" justify="center"> | ||
<c-card> | ||
<div flex> | ||
<div> | ||
<n-form-item label="Trim list items" label-placement="left" label-width="150" :show-feedback="false" mb-2> | ||
<n-switch v-model:value="conversionConfig.trimItems" /> | ||
</n-form-item> | ||
<n-form-item label="Remove duplicates" label-placement="left" label-width="150" :show-feedback="false" mb-2> | ||
<n-switch v-model:value="conversionConfig.removeDuplicates" data-test-id="removeDuplicates" /> | ||
</n-form-item> | ||
<n-form-item | ||
label="Convert to lowercase" | ||
label-placement="left" | ||
label-width="150" | ||
:show-feedback="false" | ||
mb-2 | ||
> | ||
<n-switch v-model:value="conversionConfig.lowerCase" /> | ||
</n-form-item> | ||
<n-form-item label="Keep line breaks" label-placement="left" label-width="150" :show-feedback="false" mb-2> | ||
<n-switch v-model:value="conversionConfig.keepLineBreaks" /> | ||
</n-form-item> | ||
</div> | ||
<div flex-1> | ||
<n-form-item label="Sort list" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||
<n-select | ||
v-model:value="conversionConfig.sortList" | ||
:options="sortOrderOptions" | ||
clearable | ||
w-full | ||
:disabled="conversionConfig.reverseList" | ||
data-test-id="sortList" | ||
placeholder="Sort alphabetically" | ||
/> | ||
</n-form-item> | ||
|
||
<n-form-item label="Separator" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||
<n-input v-model:value="conversionConfig.separator" placeholder="," /> | ||
</n-form-item> | ||
|
||
<n-form-item label="Wrap item" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||
<n-input-group> | ||
<n-input | ||
v-model:value="conversionConfig.itemPrefix" | ||
placeholder="Item prefix" | ||
data-test-id="itemPrefix" | ||
/> | ||
<n-input | ||
v-model:value="conversionConfig.itemSuffix" | ||
placeholder="Item suffix" | ||
data-test-id="itemSuffix" | ||
/> | ||
</n-input-group> | ||
</n-form-item> | ||
<n-form-item label="Wrap list" label-placement="left" label-width="120" :show-feedback="false" mb-2> | ||
<n-input-group> | ||
<n-input | ||
v-model:value="conversionConfig.listPrefix" | ||
placeholder="List prefix" | ||
data-test-id="listPrefix" | ||
/> | ||
<n-input | ||
v-model:value="conversionConfig.listSuffix" | ||
placeholder="List suffix" | ||
data-test-id="listSuffix" | ||
/> | ||
</n-input-group> | ||
</n-form-item> | ||
</div> | ||
</div> | ||
</c-card> | ||
</n-space> | ||
</div> | ||
<format-transformer | ||
input-label="Your input data" | ||
input-placeholder="Paste your input data here..." | ||
output-label="Your transformed data" | ||
:transformer="transformer" | ||
/> | ||
</template> | ||
|
||
<script setup lang="ts"> | ||
import { useStorage } from '@vueuse/core'; | ||
import { convert } from './list-converter.models'; | ||
import type { ConvertOptions } from './list-converter.types'; | ||
const sortOrderOptions = [ | ||
{ | ||
label: 'Sort ascending', | ||
value: 'asc', | ||
disabled: false, | ||
}, | ||
{ | ||
label: 'Sort descending', | ||
value: 'desc', | ||
disabled: false, | ||
}, | ||
]; | ||
const conversionConfig = useStorage<ConvertOptions>('list-converter:conversionConfig', { | ||
lowerCase: false, | ||
trimItems: true, | ||
removeDuplicates: true, | ||
keepLineBreaks: false, | ||
itemPrefix: '', | ||
itemSuffix: '', | ||
listPrefix: '', | ||
listSuffix: '', | ||
reverseList: false, | ||
sortList: null, | ||
separator: ', ', | ||
}); | ||
const transformer = (value: string) => { | ||
return convert(value, conversionConfig.value); | ||
}; | ||
</script> | ||
|
||
<style lang="less" scoped></style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export { byOrder }; | ||
|
||
function byOrder({ order }: { order: 'asc' | 'desc' | null | undefined }) { | ||
return (a: string, b: string) => { | ||
return order === 'asc' ? a.localeCompare(b) : b.localeCompare(a); | ||
}; | ||
} |