From 16e075502efe221f34cc6a301294e75a2fcfd0b6 Mon Sep 17 00:00:00 2001 From: Amour1688 <31695475+Amour1688@users.noreply.github.com> Date: Wed, 4 Mar 2020 11:34:15 +0800 Subject: [PATCH] feat: add mentions (#1790) * feat: mentions style * feat: theme default * feat: add mentions component * feat: mentions API * feat: add unit test for mentions * feat: update mentions demo * perf: model and inheritAttrs for mentions * perf: use getComponentFromProp instead of this.$props * perf: mentions rm defaultProps * feat: rm rows in mentionsProps * fix: mentions keyDown didn't work * docs: update mentions api * perf: mentions code --- components/index.js | 4 + .../__test__/__snapshots__/demo.test.js.snap | 46 +++ components/mentions/__test__/demo.test.js | 3 + components/mentions/__test__/index.test.js | 91 +++++ components/mentions/demo/async.md | 65 ++++ components/mentions/demo/basic.md | 35 ++ components/mentions/demo/form.md | 89 +++++ components/mentions/demo/index.vue | 48 +++ components/mentions/demo/placement.md | 21 ++ components/mentions/demo/prefix.md | 48 +++ components/mentions/demo/readonly.md | 41 +++ components/mentions/index.en-US.md | 39 +++ components/mentions/index.jsx | 193 +++++++++++ components/mentions/index.zh-CN.md | 39 +++ components/mentions/style/index.js | 6 + components/mentions/style/index.less | 167 +++++++++ components/style.js | 1 + components/style/themes/default.less | 6 + components/vc-mentions/index.js | 6 + components/vc-mentions/src/DropdownMenu.jsx | 62 ++++ components/vc-mentions/src/KeywordTrigger.jsx | 70 ++++ components/vc-mentions/src/Mentions.jsx | 322 ++++++++++++++++++ components/vc-mentions/src/Option.jsx | 15 + components/vc-mentions/src/mentionsProps.js | 39 +++ components/vc-mentions/src/placement.js | 1 + components/vc-mentions/src/util.js | 109 ++++++ site/components.js | 2 + site/demo.js | 6 + site/demoRoutes.js | 8 + tests/__snapshots__/index.test.js.snap | 1 + 30 files changed, 1583 insertions(+) create mode 100644 components/mentions/__test__/__snapshots__/demo.test.js.snap create mode 100644 components/mentions/__test__/demo.test.js create mode 100644 components/mentions/__test__/index.test.js create mode 100644 components/mentions/demo/async.md create mode 100644 components/mentions/demo/basic.md create mode 100644 components/mentions/demo/form.md create mode 100644 components/mentions/demo/index.vue create mode 100644 components/mentions/demo/placement.md create mode 100644 components/mentions/demo/prefix.md create mode 100644 components/mentions/demo/readonly.md create mode 100644 components/mentions/index.en-US.md create mode 100644 components/mentions/index.jsx create mode 100644 components/mentions/index.zh-CN.md create mode 100644 components/mentions/style/index.js create mode 100644 components/mentions/style/index.less create mode 100644 components/vc-mentions/index.js create mode 100644 components/vc-mentions/src/DropdownMenu.jsx create mode 100644 components/vc-mentions/src/KeywordTrigger.jsx create mode 100644 components/vc-mentions/src/Mentions.jsx create mode 100644 components/vc-mentions/src/Option.jsx create mode 100644 components/vc-mentions/src/mentionsProps.js create mode 100644 components/vc-mentions/src/placement.js create mode 100644 components/vc-mentions/src/util.js diff --git a/components/index.js b/components/index.js index 535379fce..dc581a4e6 100644 --- a/components/index.js +++ b/components/index.js @@ -73,6 +73,8 @@ import { default as message } from './message'; import { default as Menu } from './menu'; +import { default as Mentions } from './mentions'; + import { default as Modal } from './modal'; import { default as notification } from './notification'; @@ -171,6 +173,7 @@ const components = [ List, LocaleProvider, Menu, + Mentions, Modal, Pagination, Popconfirm, @@ -258,6 +261,7 @@ export { List, LocaleProvider, Menu, + Mentions, Modal, Pagination, Popconfirm, diff --git a/components/mentions/__test__/__snapshots__/demo.test.js.snap b/components/mentions/__test__/__snapshots__/demo.test.js.snap new file mode 100644 index 000000000..f34248c65 --- /dev/null +++ b/components/mentions/__test__/__snapshots__/demo.test.js.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders ./components/mentions/demo/async.md correctly 1`] = `
`; + +exports[`renders ./components/mentions/demo/basic.md correctly 1`] = `
`; + +exports[`renders ./components/mentions/demo/form.md correctly 1`] = ` +
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+`; + +exports[`renders ./components/mentions/demo/placement.md correctly 1`] = `
`; + +exports[`renders ./components/mentions/demo/prefix.md correctly 1`] = `
`; + +exports[`renders ./components/mentions/demo/readonly.md correctly 1`] = ` +
+
+
+
+
+
+`; diff --git a/components/mentions/__test__/demo.test.js b/components/mentions/__test__/demo.test.js new file mode 100644 index 000000000..1946e5565 --- /dev/null +++ b/components/mentions/__test__/demo.test.js @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('mentions'); diff --git a/components/mentions/__test__/index.test.js b/components/mentions/__test__/index.test.js new file mode 100644 index 000000000..a9625d44d --- /dev/null +++ b/components/mentions/__test__/index.test.js @@ -0,0 +1,91 @@ +import { mount } from '@vue/test-utils'; +import Vue from 'vue'; +import Mentions from '..'; +import { asyncExpect } from '@/tests/utils'; +import focusTest from '../../../tests/shared/focusTest'; + +const { getMentions } = Mentions; + +function $$(className) { + return document.body.querySelectorAll(className); +} + +function triggerInput(wrapper, text = '') { + wrapper.find('textarea').element.value = text; + wrapper.find('textarea').element.selectionStart = text.length; + wrapper.find('textarea').trigger('keydown'); + wrapper.find('textarea').trigger('change'); + wrapper.find('textarea').trigger('keyup'); +} + +describe('Mentions', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('getMentions', () => { + const mentions = getMentions('@light #bamboo cat', { prefix: ['@', '#'] }); + expect(mentions).toEqual([ + { + prefix: '@', + value: 'light', + }, + { + prefix: '#', + value: 'bamboo', + }, + ]); + }); + + it('focus', () => { + const onFocus = jest.fn(); + const onBlur = jest.fn(); + + const wrapper = mount({ + render() { + return ; + }, + }); + wrapper.find('textarea').trigger('focus'); + expect(wrapper.find('.ant-mentions').classes('ant-mentions-focused')).toBeTruthy(); + expect(onFocus).toHaveBeenCalled(); + + wrapper.find('textarea').trigger('blur'); + jest.runAllTimers(); + expect(wrapper.classes()).not.toContain('ant-mentions-focused'); + expect(onBlur).toHaveBeenCalled(); + }); + + it('loading', done => { + const wrapper = mount( + { + render() { + return ; + }, + }, + { sync: false }, + ); + triggerInput(wrapper, '@'); + Vue.nextTick(() => { + mount( + { + render() { + return wrapper.find({ name: 'Trigger' }).vm.getComponent(); + }, + }, + { sync: false }, + ); + Vue.nextTick(() => { + expect($$('.ant-mentions-dropdown-menu-item').length).toBeTruthy(); + expect($$('.ant-spin')).toBeTruthy(); + done(); + }); + }); + }); + + focusTest(Mentions); +}); diff --git a/components/mentions/demo/async.md b/components/mentions/demo/async.md new file mode 100644 index 000000000..747db1c42 --- /dev/null +++ b/components/mentions/demo/async.md @@ -0,0 +1,65 @@ + +#### 异步加载 +匹配内容列表为异步返回时。 + + + +#### Asynchronous loading +async. + + +```tpl + + + +