Skip to content

Commit

Permalink
feat: added ellipsis component (#248)
Browse files Browse the repository at this point in the history
* feat: added ellipsis component

* test: added ellipsis component unit test

* docs: added ellipsis component document
  • Loading branch information
baiwusanyu-c authored Sep 14, 2023
1 parent 761aa46 commit 7aea050
Show file tree
Hide file tree
Showing 16 changed files with 444 additions and 3 deletions.
11 changes: 11 additions & 0 deletions components/Ellipsis/__test__/__snapshots__/ellipsis.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Test: KEllipsis > props: cls 1`] = `"<div><div aria-hidden=\\"true\\"><div class=\\"k-ellipsis k-ellipsis--base k-ellipsis--test\\" slot=\\"triggerEl\\" aria-hidden=\\"true\\" style=\\"\\">...</div></div> </div>"`;
exports[`Test: KEllipsis > props: content 1`] = `"<div><div aria-hidden=\\"true\\"><div class=\\"k-ellipsis k-ellipsis--base\\" slot=\\"triggerEl\\" aria-hidden=\\"true\\" style=\\"\\">我言秋日胜春朝</div></div> <div class=\\"k-popover--base k-popover--base__top k-popover--base__top__dark k-popover--base__dark svelte-1dixidq\\" data-popper-placement=\\"top\\" aria-hidden=\\"true\\" style=\\"position: fixed; left: 0px; top: 0px; margin: 0px; animation: __svelte_185617033_0 200ms linear 0ms 1 both; bottom: 0px; transform: translate(0px, -8px);\\" data-popper-reference-hidden=\\"\\" data-popper-escaped=\\"\\"><span slot=\\"contentEl\\">自古逢秋悲寂寥</span> <div k-popover-arrow=\\"true\\" class=\\"svelte-1dixidq\\" data-popper-arrow-top=\\"\\"></div></div></div>"`;
exports[`Test: KEllipsis > props: expand 1`] = `"<div><div aria-hidden=\\"true\\"><div class=\\"k-ellipsis k-ellipsis--base\\" slot=\\"triggerEl\\" aria-hidden=\\"true\\" style=\\"-webkit-line-clamp: 3; display: -webkit-inline-box; overflow: hidden;\\">我言秋日...</div></div> </div>"`;
exports[`Test: KEllipsis > props: num 1`] = `"<div><div aria-hidden=\\"true\\"><div class=\\"k-ellipsis k-ellipsis--base\\" slot=\\"triggerEl\\" aria-hidden=\\"true\\" style=\\"\\">我言秋日...</div></div> </div>"`;
exports[`Test: KEllipsis > props: text 1`] = `"<div><div aria-hidden=\\"true\\"><div class=\\"k-ellipsis k-ellipsis--base\\" slot=\\"triggerEl\\" aria-hidden=\\"true\\" style=\\"\\">我言秋日胜春朝</div></div> </div>"`;
124 changes: 124 additions & 0 deletions components/Ellipsis/__test__/ellipsis.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { afterEach, expect, test, describe, beforeEach, vi } from 'vitest';
import KEllipsis from '../src';
import { tick } from 'svelte';

let host: HTMLElement;

const initHost = () => {
host = document.createElement('div');
host.setAttribute('id', 'host');
document.body.appendChild(host);
};
beforeEach(() => {
initHost();
vi.useFakeTimers();
});
afterEach(() => {
host.remove();
vi.restoreAllMocks();
});

describe('Test: KEllipsis', () => {
test('props: cls', async () => {
const instance = new KEllipsis({
target: host,
props: {
cls: 'k-ellipsis--test'
}
});
expect(instance).toBeTruthy();
expect((host as HTMLElement)!.innerHTML.includes('k-ellipsis--test')).toBeTruthy();
expect(host.innerHTML).matchSnapshot();
});

test('props: content', async () => {
const instance = new KEllipsis({
target: host,
props: {
content: '自古逢秋悲寂寥',
text: '我言秋日胜春朝',
num: 0
}
});
expect(instance).toBeTruthy();
const elm = host.querySelector('[slot="triggerEl"]');
elm.dispatchEvent(new Event('mouseenter', { bubbles: true }));
await tick();
await vi.advanceTimersByTimeAsync(300);
expect(document.body.innerHTML.includes('我言秋日胜春朝')).toBeTruthy();
expect(host.innerHTML).matchSnapshot();
});

test('props: text', async () => {
const instance = new KEllipsis({
target: host,
props: {
content: '自古逢秋悲寂寥',
text: '我言秋日胜春朝',
num: 0
}
});
expect(instance).toBeTruthy();
expect((host as HTMLElement)!.innerHTML.includes('我言秋日胜春朝')).toBeTruthy();
expect(host.innerHTML).matchSnapshot();
});

test('props: num', async () => {
const instance = new KEllipsis({
target: host,
props: {
content: '自古逢秋悲寂寥',
text: '我言秋日胜春朝',
num: 3
}
});
expect(instance).toBeTruthy();
expect((host as HTMLElement)!.innerHTML.includes('我言秋日...')).toBeTruthy();
expect(host.innerHTML).matchSnapshot();
});

test('props: expand', async () => {
const instance = new KEllipsis({
target: host,
props: {
content: '自古逢秋悲寂寥',
text: '我言秋日胜春朝',
num: 3,
expand: true,
lineClamp: 3
}
});
expect(instance).toBeTruthy();
const elm = host.querySelector('[slot="triggerEl"]');
expect(elm.style.display === '-webkit-inline-box').toBeTruthy();
elm.dispatchEvent(new Event('click', { bubbles: true }));
await tick();
expect(elm.style.display === '').toBeTruthy();
elm.dispatchEvent(new Event('click', { bubbles: true }));
await tick();
expect(elm.style.display === '-webkit-inline-box').toBeTruthy();
expect(host.innerHTML).matchSnapshot();
});

test('props: placement', async () => {
const instance = new KEllipsis({
target: host,
props: {
content: '自古逢秋悲寂寥',
text: '我言秋日胜春朝',
placement: 'center',
num: 3
}
});
expect(instance).toBeTruthy();
expect((host as HTMLElement)!.innerHTML.includes('我言...春朝')).toBeTruthy();

instance.$set({ placement: 'left' });
await tick();
expect((host as HTMLElement)!.innerHTML.includes('...日胜春朝')).toBeTruthy();

instance.$set({ placement: 'right' });
await tick();
expect((host as HTMLElement)!.innerHTML.includes('我言秋日...')).toBeTruthy();
});
});
43 changes: 43 additions & 0 deletions components/Ellipsis/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@ikun-ui/ellipsis",
"version": "0.0.9-beta.6",
"type": "module",
"main": "./src/index.ts",
"types": "src/index.ts",
"keywords": [
"svelte",
"svelte3",
"web component",
"component",
"react",
"vue",
"svelte-kit",
"dx"
],
"scripts": {
"build": "npm run build:js && npm run build:svelte",
"build:js": "tsc -p . --outDir dist/ --rootDir src/",
"build:svelte": "svelte-strip strip src/ dist",
"publish:npm": "pnpm publish --no-git-checks --access public"
},
"publishConfig": {
"access": "public",
"main": "dist/index.js",
"module": "dist/index.js",
"svelte": "dist/index.js",
"types": "src/index.ts"
},
"dependencies": {
"@ikun-ui/icon": "workspace:*",
"@ikun-ui/utils": "workspace:*",
"@ikun-ui/tooltip": "workspace:*",
"clsx": "^2.0.0",
"baiwusanyu-utils": "^1.0.14"
},
"devDependencies": {
"@tsconfig/svelte": "^5.0.0",
"svelte-strip": "^2.0.0",
"tslib": "^2.6.1",
"typescript": "^5.1.6"
}
}
77 changes: 77 additions & 0 deletions components/Ellipsis/src/index.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts">
import { getPrefixCls } from '@ikun-ui/utils';
import { KTooltip } from '@ikun-ui/tooltip';
import { clsx } from 'clsx';
import type { KEllipsisProps } from './types';
export let content: KEllipsisProps['content'] = '';
export let text: KEllipsisProps['text'] = '';
export let expand: KEllipsisProps['expand'] = false;
// TODO: How to mock this in vitest ?
export let lineClamp: KEllipsisProps['lineClamp'] = null;
export let num: KEllipsisProps['num'] = 5;
export let placement: KEllipsisProps['placement'] = 'right';
export let cls: KEllipsisProps['cls'] = '';
export let attrs: KEllipsisProps['attrs'] = {};
let isExpand = false;
let textCache = '';
let textInner = text;
let placementInner = placement;
const handleText = () => {
if (num === 0) return;
if (placementInner === 'right') textInner = `${text?.slice(0, text?.length - num)}...`;
if (placementInner === 'center') {
const elpNumCenter = num / 2;
const lenCenter = text?.length / 2;
const subStrHead = text?.substring(0, lenCenter - elpNumCenter);
const subStrFoot = text?.substring(lenCenter + elpNumCenter, text.length);
textInner = `${subStrHead}...${subStrFoot}`;
}
if (placementInner === 'left') textInner = `...${text.substring(num, text.length)}`;
};
$: {
textCache = text;
placementInner = placement;
handleText();
}
let styleStr = `-webkit-line-clamp:${lineClamp};
display:-webkit-inline-box;
-webkit-box-orient:vertical;
overflow:hidden;`;
let styleContent = !lineClamp ? '' : styleStr;
const handleExpand = () => {
if (!expand) return;
isExpand = !isExpand;
styleContent = isExpand ? '' : styleStr;
isExpand && (textInner = textCache);
!isExpand && handleText();
};
// class
const prefixCls = getPrefixCls('ellipsis');
$: cnames = clsx(
prefixCls,
{
[`${prefixCls}--base`]: true
},
cls
);
</script>

<KTooltip {content} placement="top">
<div
class={cnames}
slot="triggerEl"
{...$$restProps}
{...attrs}
aria-hidden="true"
style={styleContent}
on:click={handleExpand}
>
{textInner}
</div>
</KTooltip>
5 changes: 5 additions & 0 deletions components/Ellipsis/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="./types" />
import Ellipsis from './index.svelte';
export { Ellipsis as KEllipsis };

export default Ellipsis;
12 changes: 12 additions & 0 deletions components/Ellipsis/src/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// <reference types="svelte" />
import type { ClassValue } from 'clsx';
export type KEllipsisProps = {
cls: ClassValue;
attrs: Record<string, string>;
content: string;
text: string;
expand: boolean;
lineClamp: number | null;
num: number;
placement: 'left' | 'right' | 'center';
};
11 changes: 11 additions & 0 deletions components/Ellipsis/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "@tsconfig/svelte/tsconfig.json",

"compilerOptions": {
"noImplicitAny": true,
"strict": true,
"declaration": true
},
"include": ["src/**/*.ts", "src/**/*.svelte"],
"exclude": ["node_modules/*", "**/*.spec.ts"]
}
4 changes: 4 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ const components = [
text: 'Collapse',
link: '/components/KCollapse'
},
{
text: 'Ellipsis',
link: '/components/KEllipsis'
},
{
text: 'Tag',
link: '/components/KTag'
Expand Down
64 changes: 64 additions & 0 deletions docs/components/KEllipsis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
---
title: KEllipsis
lang: en-US
---

# KEllipsis

It can omit your text to a certain extent.

## Install

::: code-group

```bash [pnpm]
pnpm add @ikun-ui/ellipsis
```

```bash [yarn]
yarn add @ikun-ui/ellipsis
```

```bash [npm]
npm install @ikun-ui/ellipsis
```

:::

## Basic usage

Basic single line ellipsis with tooltip.
`content` is the text you need to display in the tooltip, `text` is the text you need to abbreviate.

<demo src="../../../../example/ellipsis/basic.svelte" github='Ellipsis'></demo>

## Omit the number of characters

You can specify the number of words you need to omit through `num`.

<demo src="../../../../example/ellipsis/num.svelte" github='Ellipsis'></demo>

## omitted position

`placement` can specify the position where the ellipsis appears

<demo src="../../../../example/ellipsis/position.svelte" github='Ellipsis'></demo>

## Omit multiple lines

See [caniuse <span class="i-carbon-link text-12px" />](https://caniuse.com/?search=line-clamp) for compatibility.

<demo src="../../../../example/ellipsis/line-clamp.svelte" github='Ellipsis'></demo>

## Ellipsis Props

| Name | Type | Default | Description |
| --------- | ------------------------------- | ------- | -------------------------------------------------------------- |
| content | `string` | `''` | The text content displayed by tooltip. |
| text | `string` | `''` | Text content that needs to be omitted. |
| expand | `boolean` | `false` | Abbreviated content trigger event to expand to the full text.. |
| lineClamp | `number \| null` | `null` | Maximum lines. |
| num | `number` | `5` | Number of characters to omit. |
| placement | `'left' \| 'right' \| 'center'` | `right` | Ellipsis display position. |
| cls | `string` | `''` | Additional class for |
| attrs | `Record<string, string>` | `{}` | Additional attributes |
8 changes: 8 additions & 0 deletions docs/example/ellipsis/basic.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
import { KEllipsis } from '@ikun-ui/ellipsis';
</script>

<KEllipsis
text="When I look into your eyes, It's like watching the night sky"
content="Or a beautiful sunrise, There’s so much they hold"
/>
12 changes: 12 additions & 0 deletions docs/example/ellipsis/line-clamp.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
import { KEllipsis } from '@ikun-ui/ellipsis';
</script>

<div style="width: 200px">
<KEllipsis
text="I'll be here patiently waiting, To see what you find, Cause even the stars they burn, Some even fall to the earth"
expand
lineClamp={2}
content="I'll be here patiently waiting, To see what you find, Cause even the stars they burn, Some even fall to the earth"
/>
</div>
9 changes: 9 additions & 0 deletions docs/example/ellipsis/num.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<script>
import { KEllipsis } from '@ikun-ui/ellipsis';
</script>

<KEllipsis
text="And just like them old stars, I see that you've come so far"
num={18}
content="To be right where you are, How old is your soul?"
/>
Loading

0 comments on commit 7aea050

Please sign in to comment.