summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gascou-Vaillancourt <paul.gascvail@gmail.com>2019-04-23 16:04:14 -0400
committerPaul Gascou-Vaillancourt <paul.gascvail@gmail.com>2019-04-25 10:30:46 -0400
commitf7a498ccc3bcbf92cc1043b4d9ecf2f3091aae17 (patch)
tree65f9bfea4a3f46dc1e96d5204883e304244e964d
parent1e2d4a217a06da4fc92efa5c6f2e13873b2f2067 (diff)
downloadgitlab-ce-10450-friendly-wrap-component.tar.gz
Add FriendlyWrap component10450-friendly-wrap-component
The FriendlyWrap component wraps text at specific places by inserting a <wbr> element after each symbols occurence
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js8
-rw-r--r--app/assets/javascripts/vue_shared/components/friendly_wrap.vue31
-rw-r--r--changelogs/unreleased/10450-friendly-wrap-component.yml5
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js7
-rw-r--r--spec/frontend/vue_shared/components/friendly_wrap_spec.js63
5 files changed, 114 insertions, 0 deletions
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index cc1d85fd97d..09d9ae35e70 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -192,3 +192,11 @@ export const truncateNamespace = (string = '') => {
return namespace;
};
+
+/**
+ * Escapes RegExp special characters
+ *
+ * @param {String} str
+ * @returns String
+ */
+export const escapeRegExp = str => str.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
diff --git a/app/assets/javascripts/vue_shared/components/friendly_wrap.vue b/app/assets/javascripts/vue_shared/components/friendly_wrap.vue
new file mode 100644
index 00000000000..dfebd67c54a
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/friendly_wrap.vue
@@ -0,0 +1,31 @@
+<script>
+import _ from 'underscore';
+import { escapeRegExp } from '../../lib/utils/text_utility';
+
+export default {
+ props: {
+ text: {
+ type: String,
+ required: true,
+ },
+ symbols: {
+ type: Array,
+ required: false,
+ default: () => ['/'],
+ },
+ },
+ computed: {
+ displayText() {
+ const appendWordBreak = (str, symbol) =>
+ str.replace(new RegExp(`(${symbol})`, 'g'), `$1<wbr>`);
+ return _.uniq(this.symbols)
+ .map(escapeRegExp)
+ .reduce(appendWordBreak, _.escape(this.text));
+ },
+ },
+};
+</script>
+
+<template>
+ <span class="text-break" v-html="displayText"></span>
+</template>
diff --git a/changelogs/unreleased/10450-friendly-wrap-component.yml b/changelogs/unreleased/10450-friendly-wrap-component.yml
new file mode 100644
index 00000000000..f10c15a2931
--- /dev/null
+++ b/changelogs/unreleased/10450-friendly-wrap-component.yml
@@ -0,0 +1,5 @@
+---
+title: Add FriendlyWrap component
+merge_request: 27600
+author:
+type: other
diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index 0878c1de095..e31cbbaf99e 100644
--- a/spec/frontend/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
@@ -176,4 +176,11 @@ describe('text_utility', () => {
});
});
});
+
+ describe('escapeRegExp', () => {
+ it('escapes regexp special characters', () => {
+ const str = '[\\^$.*+?()[]{}|]';
+ expect(textUtils.escapeRegExp(str)).toBe('\\[\\\\\\^\\$\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\]');
+ });
+ });
});
diff --git a/spec/frontend/vue_shared/components/friendly_wrap_spec.js b/spec/frontend/vue_shared/components/friendly_wrap_spec.js
new file mode 100644
index 00000000000..170820dfadc
--- /dev/null
+++ b/spec/frontend/vue_shared/components/friendly_wrap_spec.js
@@ -0,0 +1,63 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import FriendlyWrap from '~/vue_shared/components/friendly_wrap';
+
+const localVue = createLocalVue();
+
+describe('Friendly wrap component', () => {
+ let wrapper;
+
+ const createComponent = props => {
+ wrapper = shallowMount(FriendlyWrap, {
+ localVue,
+ propsData: props,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('wraps text on slashes by default', () => {
+ const text = '/some/file/path';
+ const textWrapped = '/<wbr>some/<wbr>file/<wbr>path';
+ createComponent({
+ text,
+ });
+
+ expect(wrapper.text()).toBe(text);
+ expect(wrapper.html()).toMatch(textWrapped);
+ });
+
+ it('supports backslashes', () => {
+ const text = '\\some\\long\\file\\path';
+ const textWrapped = '\\<wbr>some\\<wbr>long\\<wbr>file\\<wbr>path';
+ createComponent({
+ text,
+ symbols: ['\\'],
+ });
+ expect(wrapper.text()).toBe(text);
+ expect(wrapper.html()).toMatch(textWrapped);
+ });
+
+ it('accepts multiple symbols', () => {
+ const text = 'some;text-that.needs;to-be.wrapped';
+ const textWrapped = 'some;<wbr>text-<wbr>that.<wbr>needs;<wbr>to-<wbr>be.<wbr>wrapped';
+ createComponent({
+ text,
+ symbols: [';', '-', '.'],
+ });
+ expect(wrapper.text()).toBe(text);
+ expect(wrapper.html()).toMatch(textWrapped);
+ });
+
+ it('works with words', () => {
+ const text = 'it goes on and on and on and on';
+ const textWrapped = 'it goes on and<wbr> on and<wbr> on and<wbr> on';
+ createComponent({
+ text,
+ symbols: ['and'],
+ });
+ expect(wrapper.text()).toBe(text);
+ expect(wrapper.html()).toMatch(textWrapped);
+ });
+});