From 35512d0d654aa36201b8348cbe33990addf09539 Mon Sep 17 00:00:00 2001 From: Mitch Robb Date: Fri, 3 Jun 2016 00:57:40 -0700 Subject: [PATCH] first stab at merging global and instance linkifyIt customizations --- src/Linkify.jsx | 68 +++++++++++++++------ src/__tests__/Linkify-test.js | 107 +++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 19 deletions(-) diff --git a/src/Linkify.jsx b/src/Linkify.jsx index 4142e12..6ed4a9f 100644 --- a/src/Linkify.jsx +++ b/src/Linkify.jsx @@ -2,8 +2,33 @@ import React from 'react'; import LinkifyIt from 'linkify-it'; import tlds from 'tlds'; -export const linkify = new LinkifyIt(); -linkify.tlds(tlds); +const globalCustomizations = { + add: [], + tlds: [], + set: [] +}; + +export const config = { + add: (...args) => { + globalCustomizations.add.push(args); + return config; + }, + tlds: (...args) => { + globalCustomizations.tlds.push(args); + return config; + }, + set: (...args) => { + globalCustomizations.set.push(args); + return config; + }, + resetAll: () => { + for (let type in globalCustomizations) { + globalCustomizations[type] = []; + } + + return config; + } +}; class Linkify extends React.Component { static MATCH = 'LINKIFY_MATCH' @@ -20,7 +45,10 @@ class Linkify extends React.Component { validate: React.PropTypes.func.isRequired, normalize: React.PropTypes.func.isRequired }) - ) + ), + fuzzyLink: React.PropTypes.bool, + fuzzyIP: React.PropTypes.bool, + fuzzyEmail: React.PropTypes.bool } static defaultProps = { @@ -35,33 +63,37 @@ class Linkify extends React.Component { } componentDidUpdate(nextProps) { - if (this.props.handlers !== nextProps.handlers) { - this.addCustomHandlers(); - } + this.addCustomHandlers(); } addCustomHandlers() { const { handlers } = this.props; - if (handlers.length) { - this.linkify = new LinkifyIt(); - this.linkify.tlds(tlds); + this.linkify = this.linkify || new LinkifyIt(); + this.linkify.tlds(tlds); - handlers.forEach(handler => { - this.linkify.add(handler.prefix, { - validate: handler.validate, - normalize: handler.normalize - }); - }); + // add global customizations + for (let type in globalCustomizations) { + globalCustomizations[type].forEach(c => this.linkify[type](...c)) } + + // add instance customizations + (handlers || []).forEach((handler) => { + this.linkify.add(handler.prefix, { + validate: handler.validate, + normalize: handler.normalize + }); + }); + + ['fuzzyLink', 'fuzzyIP', 'fuzzyEmail'].forEach(f => { + typeof this.props[f] === 'boolean' && this.linkify.set({ [f]: this.props[f] }) + }) } parseCounter = 0 getMatches(string) { - const linkifyInstance = this.linkify || linkify; - - return linkifyInstance.match(string); + return this.linkify.match(string); } parseString(string) { diff --git a/src/__tests__/Linkify-test.js b/src/__tests__/Linkify-test.js index 4f2578d..528a986 100644 --- a/src/__tests__/Linkify-test.js +++ b/src/__tests__/Linkify-test.js @@ -5,6 +5,8 @@ let TestUtils = require('react-addons-test-utils'); describe('Linkify', () => { let Linkify = require('../Linkify.jsx').default; + let linkifyCustomizations = require('../Linkify.jsx').config; + describe('#parseString', () => { let linkify = TestUtils.renderIntoDocument(); @@ -121,7 +123,7 @@ describe('Linkify', () => { }); }); - describe('#addCustomHandlers', () => { + describe('LinkifyIt config', () => { it('should match a custom handler added through the "handlers" prop', () => { const linkify = TestUtils.renderIntoDocument( { expect(output[3].props.children).toEqual(input[3]); expect(output[4]).toEqual(input[4]); }) + + it('should apply global customizations', () => { + linkifyCustomizations + .resetAll() + .tlds('linkify', true) + .add('@', { + validate() { + return 7; + }, + normalize(match) { + match.url = 'https://twitter.com/' + match.url.replace(/^@/, ''); + } + }) + const linkify = TestUtils.renderIntoDocument( + + ); + + const input = ['this is an ', '@mention', ' and ', 'test.linkify', ' TLD handler']; + const output = linkify.parseString(input.join('')); + + expect(output[0]).toEqual(input[0]); + expect(output[1].type).toEqual('a'); + expect(output[1].props.href).toEqual(`https://twitter.com/mention`); + expect(output[1].props.children).toEqual(input[1]); + + expect(output[2]).toEqual(input[2]); + expect(output[3].type).toEqual('a'); + expect(output[3].props.href).toEqual(`http://test.linkify`); + expect(output[3].props.children).toEqual(input[3]); + expect(output[4]).toEqual(input[4]); + }); + + it('should merge global and instance handlers', () => { + linkifyCustomizations + .resetAll() + .add('@', { + validate() { + return 7; + }, + normalize(match) { + match.url = 'https://twitter.com/' + match.url.replace(/^@/, ''); + } + }) + const linkify = TestUtils.renderIntoDocument( + + + ); + + const input = ['this is an ', '@mention', ' and ', '$mention', ' handler']; + const output = linkify.parseString(input.join('')); + + expect(output[0]).toEqual(input[0]); + expect(output[1].type).toEqual('a'); + expect(output[1].props.href).toEqual(`https://twitter.com/mention`); + expect(output[1].props.children).toEqual(input[1]); + + expect(output[2]).toEqual(input[2]); + expect(output[3].type).toEqual('a'); + expect(output[3].props.href).toEqual(`https://blingtwitter.com/mention`); + expect(output[3].props.children).toEqual(input[3]); + expect(output[4]).toEqual(input[4]); + }); + + it('should set fuzzy* options', () => { + const linkify = TestUtils.renderIntoDocument( + + ); + + const linkInput = 'this should not match: www.test.com'; + const linkOutput = linkify.parseString(linkInput); + expect(linkOutput).toEqual(linkInput); + }); + + it('should reset global customizations', () => { + linkifyCustomizations + .add('@', { + validate() { + return 7; + }, + normalize(match) { + match.url = 'https://twitter.com/' + match.url.replace(/^@/, ''); + } + }) + .resetAll() + + const linkify = TestUtils.renderIntoDocument( + + ); + + const input = 'this @mention should not match'; + const output = linkify.parseString(input); + + expect(output).toEqual(input); + }) }); describe('#render', () => {