Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

filter for mobile or string search #128

Open
gloriaJun opened this issue Aug 20, 2021 · 0 comments
Open

filter for mobile or string search #128

gloriaJun opened this issue Aug 20, 2021 · 0 comments
Assignees

Comments

@gloriaJun
Copy link
Owner

keywrod matching

getSpecialCharEscapedRegex

export const getSpecialCharEscapedRegex = (keyword: string) => {
  const SEARCH_REGEX = '[+!@#$%^&*(),.?":{}[]|<>\\\\/]';
  const specialCharacterRegex = new RegExp(SEARCH_REGEX, 'g');
  const escapedKeyword = keyword.replace(specialCharacterRegex, '\\$&');

  return new RegExp(`(${escapedKeyword})`, 'gi');
};
describe('getSpecialCharEscapedRegex', () => {
  it.each(['tests', 'test@@', '+test@!', 'test.'])(`should be return true, keyword is '%s'`, (keyword) => {
    const re = getSpecialCharEscapedRegex(keyword);
    expect(re.test(`xxx${keyword}abc`)).toBe(true);
  });
});

getNumberByRegex

function getNumberByRegex(keyword = '') {
  if (!keyword) {
    return '';
  }
  const numberPattern = /\d+/g;
  const regRes = keyword.match(numberPattern);
  if (regRes?.length) {
    return regRes.join([]);
  }
  return '';
}

isKeywordMatched / isPhoneNumberString

export function isPhoneNumberString(keyword: string) {
  const re = new RegExp(`^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\\s\\./0-9]*$`);
  return re.test(keyword);
}

export function isKeywordMatched(title?: string | null, keyword?: string | null) {
  if (!keyword || !title) {
    return false;
  }

  const isMatched = (text: string, searchStr: string) => {
    const regexp = getSpecialCharEscapedRegex(searchStr);
    return regexp.test(text);
  };

  if (isPhoneNumberString(keyword)) {
    return isMatched(
      isPhoneNumberString(title) ? getNumberByRegex(title) : title,
      getNumberByRegex(keyword)
    );
  } else {
    return isMatched(title, keyword);
  }
}
import { isPhoneNumberString, isKeywordMatched } from './isKeywordMatched';

describe('isPhoneNumberString', () => {
  it.each([
    '66 21 234 681',
    '21-234-681',
    '21 234-681',
    '21 234 681',
    '010-1234-1234',
    '010 1234',
    '+82 010',
    '1544-1234',
    '1234 123 1234',
    '911',
  ])(`should be return true, keyword is phone number '%s'`, (keyword) => {
    const result = isPhoneNumberString(keyword);
    expect(result).toBe(true);
  });

  it.each(['66*21*234*681'])(`should be return false, keyword is not phone number '%s'`, (keyword) => {
    const result = isPhoneNumberString(keyword);
    expect(result).toBe(false);
  });
});

describe('isKeywordMatched', () => {
  it.each(['', null, undefined])(`should be return false, title is '%s'`, (title) => {
    const result = isKeywordMatched(title, 'keyword');
    expect(result).toBe(false);
  });

  it.each(['', null, undefined])(`should be return false, keyword is '%s'`, (keyword) => {
    const result = isKeywordMatched('title', keyword);
    expect(result).toBe(false);
  });

  it('should match normal keyword correctly', () => {
    const messageWithKeyword = 'messagetest';
    const meessageNoKeyword = 'message';
    const keyword = 'test';

    const resultOne = isKeywordMatched(messageWithKeyword, keyword);
    const resultTwo = isKeywordMatched(meessageNoKeyword, keyword);

    expect(resultOne).toBe(true);
    expect(resultTwo).toBe(false);
  });

  it('should match special character keyword correctly', () => {
    const messageWithKeyword = 'message@!';
    const meessageNoKeyword = 'message';
    const keyword = '@!';

    const resultOne = isKeywordMatched(messageWithKeyword, keyword);
    const resultTwo = isKeywordMatched(meessageNoKeyword, keyword);

    expect(resultOne).toBe(true);
    expect(resultTwo).toBe(false);
  });

  it.each(['0101234', '010-1234', '010 1234'])(
    `should be return true, title is phone number and keyword is phone number '%s'`,
    (keyword) => {
      const result = isKeywordMatched('82-010-1234-5678', keyword);
      expect(result).toBe(true);
    }
  );

  it.each(['82*010-1234-5678', 'test'])(
    `should be return false, title is '%s' and keyword is phone number`,
    (title) => {
      const result = isKeywordMatched(title, '0101234');
      expect(result).toBe(false);
    }
  );
});

marking with react

import React from 'react';

type IOjbectType<T> = T | null | undefined;

const isNull = <T>(obj: IOjbectType<T>): obj is null => {
  return obj === null || (typeof obj === 'string' && obj === 'null');
};

const isUndefined = <T>(obj: IOjbectType<T>): obj is undefined => {
  return typeof obj === 'undefined' || (typeof obj === 'string' && obj === 'undefined');
};

const isNullOrUndefined = <T>(obj: IOjbectType<T>): obj is null | undefined => {
  return isNull(obj) || isUndefined(obj);
};


const isEmptyString = (v: unknown): v is null | undefined => {
  return isNullOrUndefined(v) || (isString(v) && v.trim() === '');
};

function generateFromGeneral(title: string, keyword: string) {
  let matches;
  let startIdx = 0;
  const markedComponent = [];
  const keywordRegex = getSpecialCharEscapedRegex(keyword);

  while ((matches = keywordRegex.exec(title)) !== null) {
    const matchIndex = matches.index;

    if (matchIndex !== 0) {
      markedComponent.push(title.substr(startIdx, matchIndex - startIdx));
    }

    markedComponent.push(<mark>{matches[0]}</mark>);

    startIdx = keywordRegex.lastIndex;
  }

  if (startIdx < title.length) {
    markedComponent.push(title.substr(startIdx));
  }

  return markedComponent.map((item, index) => {
    return <React.Fragment key={index}>{item}</React.Fragment>;
  });
}

function generateFromPhoneNumber(title: string, keyword: string) {
  const markedComponent = [];

  const keywordNumStr = NumberUtils.getNumberByRegex(keyword);
  const keywordRegex = getSpecialCharEscapedRegex(keywordNumStr);
  const matchesWithOnlyNumber = keywordRegex.exec(getNumberByRegex(title));

  const getActualStringLength = (startIndex: number, length: number) => {
    let count = 0;

    const includedCharMatched = new RegExp('\\D+', 'gi').exec(title.substr(startIndex, length));
    if (includedCharMatched) {
      includedCharMatched.map((item) => {
        count += item.length;
      });
    }

    return length + count;
  };

  if (matchesWithOnlyNumber) {
    // check index for before matched
    let actualMatchedStartIndex = getActualStringLength(0, matchesWithOnlyNumber.index);
    while (keywordNumStr.substr(0, 1) !== title.substr(actualMatchedStartIndex, 1)) {
      actualMatchedStartIndex++;
    }

    // check matched keyword length
    const actualMatchedLength = getActualStringLength(actualMatchedStartIndex, keywordNumStr.length);
    // check index for after matched
    const actualMatchedEndIndex = actualMatchedStartIndex + actualMatchedLength;

    // console.log('### check - titleSplitByMatchedIndex', {
    //   title,
    //   keywordNumStr,
    //   actualMatchedStartIndex,
    //   actualMatchedEndIndex,
    //   actualMatchedLength,
    //   beforeMactched: title.substr(0, actualMatchedStartIndex),
    //   matched: title.substr(actualMatchedStartIndex, actualMatchedLength),
    //   afterMactched: title.substr(actualMatchedEndIndex),
    // });

    if (actualMatchedStartIndex !== 0) {
      markedComponent.push(title.substr(0, actualMatchedStartIndex));
    }

    markedComponent.push(<mark>{title.substr(actualMatchedStartIndex, actualMatchedLength)}</mark>);

    if (actualMatchedEndIndex < title.length) {
      markedComponent.push(title.substr(actualMatchedEndIndex));
    }
  }

  return markedComponent.map((item, index) => {
    return <React.Fragment key={index}>{item}</React.Fragment>;
  });
}

export function generateMarkedComponent(title: string, keyword?: string | null) {
  if (isEmptyString(title) || isEmptyString(keyword) || !isKeywordMatched(title, keyword)) {
    return title;
  }

  return isPhoneNumberString(keyword) ? generateFromPhoneNumber(title, keyword) : generateFromGeneral(title, keyword);
}
import React from 'react';

import { generateMarkedComponent } from './generateMarkedComponent';

describe('generateMarkedComponent', function () {
  it(`should be the plain text, if title is empty string`, () => {
    const title = '';
    const result = generateMarkedComponent(title, 'keyword');
    expect(result).toBe(title);
  });

  it.each(['', null, undefined])(`should be the plain text, if keyword is empty('%s)`, (keyword) => {
    const title = 'Hello, world!';
    const result = generateMarkedComponent(title, keyword);
    expect(result).toBe(title);
  });

  it(`should be return the plain text, if keyword is not matched`, () => {
    const title = 'Starbucks Coffee';
    const result = generateMarkedComponent(title, 'ABC');
    expect(result).toBe(title);
  });

  it(`should be add mark, if upper keyword is matched`, () => {
    const result = generateMarkedComponent('Starbucks Coffee', 'Star');

    expect(result.length).toBe(2);
    expect(React.isValidElement(result[0])).toBeTruthy();
    expect(result).toMatchSnapshot();
  });

  it(`should be add mark, if lower keyword is matched`, () => {
    const result = generateMarkedComponent('Starbucks Coffee', 'coff');

    expect(result.length).toBe(3);
    expect(React.isValidElement(result[1])).toBeTruthy();
    expect(result).toMatchSnapshot();
  });

  it.each(['+66223', '66'])(
    `should be add mark in the first word, if phone keyword '%s' is matched`,
    (keyword: string) => {
      const result = generateMarkedComponent('66-223-1234', keyword);

      expect(result.length).toBe(2);
      expect(React.isValidElement(result[0])).toBeTruthy();
      expect(result).toMatchSnapshot();
    }
  );

  it.each(['223', '23', '312', '123', '6223', '3123'])(
    `should be add mark in the middle word, if phone keyword '%s' is matched`,
    (keyword: string) => {
      const result = generateMarkedComponent('66-223-1234', keyword);

      expect(result.length).toBe(3);
      expect(React.isValidElement(result[1])).toBeTruthy();
      expect(result).toMatchSnapshot();
    }
  );

  it.each(['234', '3-1234'])(
    `should be add mark in the last word, if phone keyword '%s' is matched`,
    (keyword: string) => {
      const message = '66-223-1234';
      const result = generateMarkedComponent(message, keyword);

      expect(result.length).toBe(2);
      // @ts-ignore
      expect(result[0].props.children).toBe(message.split(keyword)[0]);
      expect(React.isValidElement(result[1])).toBeTruthy();
      expect(result).toMatchSnapshot();
    }
  );

  it.each(['66 123 1234', '66 123  1234', '66--123--1234', '66 - 123 - 1234', '+66-10-123-1234'])(
    `should be add mark, if message is '%s' matched with phone keyword`,
    (message: string) => {
      const keyword = '123';
      const result = generateMarkedComponent(message, keyword);

      const firstWord = message.split(new RegExp(keyword))[0];

      expect(result.length).toBe(3);
      // @ts-ignore
      expect(result[0].props.children).toBe(firstWord);
      expect(React.isValidElement(result[1])).toBeTruthy();
      // @ts-ignore
      expect(result[2].props.children).toBe(message.split(`${firstWord}${keyword}`)[1]);
      expect(result).toMatchSnapshot();
    }
  );
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant