Skip to content

Commit

Permalink
feat(list-converter): a small converter who deals with column based d…
Browse files Browse the repository at this point in the history
…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
cgoIT authored and CorentinTh committed May 7, 2023
1 parent ce3150c commit 83a7b3b
Show file tree
Hide file tree
Showing 9 changed files with 441 additions and 139 deletions.
280 changes: 141 additions & 139 deletions components.d.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { tool as base64FileConverter } from './base64-file-converter';
import { tool as base64StringConverter } from './base64-string-converter';
import { tool as basicAuthGenerator } from './basic-auth-generator';
import { tool as listConverter } from './list-converter';
import { tool as phoneParserAndFormatter } from './phone-parser-and-formatter';
import { tool as jsonDiff } from './json-diff';
import { tool as ipv4RangeExpander } from './ipv4-range-expander';
Expand Down Expand Up @@ -74,6 +75,7 @@ export const toolsByCategory: ToolCategory[] = [
textToNatoAlphabet,
yamlToJson,
jsonToYaml,
listConverter,
],
},
{
Expand Down
13 changes: 13 additions & 0 deletions src/tools/list-converter/index.ts
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'),
});
39 changes: 39 additions & 0 deletions src/tools/list-converter/list-converter.e2e.spec.ts
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'");
});
});
76 changes: 76 additions & 0 deletions src/tools/list-converter/list-converter.models.test.ts
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);
});
});
});
27 changes: 27 additions & 0 deletions src/tools/list-converter/list-converter.models.ts
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();
}
15 changes: 15 additions & 0 deletions src/tools/list-converter/list-converter.types.ts
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;
};
121 changes: 121 additions & 0 deletions src/tools/list-converter/list-converter.vue
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>
7 changes: 7 additions & 0 deletions src/utils/array.ts
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);
};
}

0 comments on commit 83a7b3b

Please sign in to comment.