diff --git a/App.js b/App.js index a76fa42..29121eb 100644 --- a/App.js +++ b/App.js @@ -24,7 +24,7 @@ export default class App extends Component { return ( @@ -111,7 +111,6 @@ const textStyles = StyleSheet.create({ textShadowColor: '#555555', textShadowRadius: 6, fontSize: 18, - fontStyle: 'italic', color: '#22AA44', }, code: { diff --git a/README.md b/README.md index d2bd6a6..ecad81d 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ import { StyledText } from 'react-native-styled-text'; ... ... @@ -70,7 +70,7 @@ import { StyledText } from 'react-native-styled-text'; ... @@ -90,7 +90,6 @@ const textStyles = StyleSheet.create({ textShadowColor: '#555555', textShadowRadius: 6, fontSize: 24, - fontStyle: 'italic', color: '#22AA44', }, }); diff --git a/docs/example-android.png b/docs/example-android.png index 791a93b..3ab769b 100644 Binary files a/docs/example-android.png and b/docs/example-android.png differ diff --git a/docs/example-ios.png b/docs/example-ios.png index e0a667f..e248638 100644 Binary files a/docs/example-ios.png and b/docs/example-ios.png differ diff --git a/docs/happyStyling.png b/docs/happyStyling.png index 8e6a380..c0eed5a 100644 Binary files a/docs/happyStyling.png and b/docs/happyStyling.png differ diff --git a/docs/welcome.png b/docs/welcome.png index 1f2166c..6d69755 100644 Binary files a/docs/welcome.png and b/docs/welcome.png differ diff --git a/lib/StyledText/lexicalAnalyzer.js b/lib/StyledText/lexicalAnalyzer.js index 78478e4..36860f5 100644 --- a/lib/StyledText/lexicalAnalyzer.js +++ b/lib/StyledText/lexicalAnalyzer.js @@ -15,6 +15,10 @@ const replaceCodes = (text: string) => { } export const scan = (text: string): Array => { + if (text === undefined || text === null || text === '') { + return [{ type: TOKEN_TEXT, lexeme: text }]; + } + const tagRegex = /<([\w-]+)>|<\/([\w-]*)>/; const tokens = []; diff --git a/lib/StyledText/renderer.js b/lib/StyledText/renderer.js index 75c94e0..733ffcf 100644 --- a/lib/StyledText/renderer.js +++ b/lib/StyledText/renderer.js @@ -2,6 +2,7 @@ import * as React from 'react'; import { Text, StyleSheet } from 'react-native'; import { parse, Mixed } from './parser'; +import { verifyTextStyles } from './utils'; const defaultStyles = { b: { @@ -15,26 +16,8 @@ const defaultStyles = { } }; -const verifyTextStyles = (mixedText: Mixed, textStyles: Object) => { - const styleNames = []; - mixedText.map(element => { - if (typeof element !== 'string') { - const index = styleNames.indexOf(element.styleName); - if (index < 0) { - styleNames.push(element.styleName); - } - } - }); - - styleNames.forEach((styleName) => { - if (!textStyles[styleName] && !defaultStyles[styleName]) { - console.warn('react-native-styled-text: style "' + styleName + '" is not defined'); - } - }); -} - const renderMixedText = (mixedText: Mixed, textStyles: Object) => mixedText.map((element, index) => ( - typeof element === 'string' + typeof element === 'string' || element === undefined || element === null ? element : React.createElement( Text, @@ -49,7 +32,7 @@ const renderMixedText = (mixedText: Mixed, textStyles: Object) => mixedText.map( export const renderStyledText = (text, style, textStyles) => { const mixedText = parse(text); const styles = textStyles || {} - verifyTextStyles(mixedText, styles); + verifyTextStyles(mixedText, styles, defaultStyles); const textElements = renderMixedText(mixedText, styles); diff --git a/lib/StyledText/utils.js b/lib/StyledText/utils.js new file mode 100644 index 0000000..35a8097 --- /dev/null +++ b/lib/StyledText/utils.js @@ -0,0 +1,35 @@ +import { Mixed } from './parser'; + +const findAllTextStyles = ( + mixedText: Mixed, + accStyleNames: Array = [] +) : Array => { + mixedText.map(element => { + if (!(typeof element === 'string' || element === undefined || element === null)) { + const index = accStyleNames.indexOf(element.styleName); + if (index < 0) { + accStyleNames.push(element.styleName); + } + + accStyleNames = findAllTextStyles(element.mixedText, accStyleNames); + } + }); + + return accStyleNames; +} + +const verifyTextStyles = ( + mixedText: Mixed, + textStyles: Object, + defaultStyles: Object +) => { + const styleNames = findAllTextStyles(mixedText); + + styleNames.forEach((styleName) => { + if (!textStyles[styleName] && !defaultStyles[styleName]) { + console.warn('react-native-styled-text: style "' + styleName + '" is not defined'); + } + }); +} + +export { verifyTextStyles }; \ No newline at end of file diff --git a/lib/__tests__/lexicalAnalyzer.spec.js b/lib/__tests__/lexicalAnalyzer.spec.js index 073ca79..c73cd6f 100644 --- a/lib/__tests__/lexicalAnalyzer.spec.js +++ b/lib/__tests__/lexicalAnalyzer.spec.js @@ -6,7 +6,7 @@ import { TOKEN_TEXT, } from '../StyledText/lexicalAnalyzer'; -describe('components/FormattedText/lexicalAnalyzer', () => { +describe('StyledText/lexicalAnalyzer', () => { describe('scan', () => { it('should return text if plain text', () => { const text = 'Testing warnings.push(msg); -describe('components/FormattedText/parser', () => { +describe('StyledText/parser', () => { beforeAll(() => { console['warn'] = warn; }); @@ -19,6 +19,27 @@ describe('components/FormattedText/parser', () => { expect(mixedText[0]).toBe(text); }); + it('should return undefined if undefined', () => { + const text = undefined; + const mixedText = parse(text); + expect(mixedText.length).toBe(1); + expect(mixedText[0]).toBe(text); + }); + + it('should return null if null', () => { + const text = undefined; + const mixedText = parse(text); + expect(mixedText.length).toBe(1); + expect(mixedText[0]).toBe(text); + }); + + it('should return "" if ""', () => { + const text = ''; + const mixedText = parse(text); + expect(mixedText.length).toBe(1); + expect(mixedText[0]).toBe(text); + }); + it('should handle text if enclosed in tags', () => { const text = 'Testing adsf'; const mixedText = parse(text); diff --git a/lib/__tests__/utils.spec.js b/lib/__tests__/utils.spec.js new file mode 100644 index 0000000..be79861 --- /dev/null +++ b/lib/__tests__/utils.spec.js @@ -0,0 +1,86 @@ +import { verifyTextStyles } from '../StyledText/utils'; + +let warnings = []; +const warn = msg => warnings.push(msg); +const textStyles = { + style1: {}, + style2: {}, +}; +const defaultStyles = { + b: {}, + i: {}, + u: {} +}; + +describe('StyledText/utils', () => { + beforeAll(() => { + console['warn'] = warn; + }); + beforeEach(() => { + warnings = []; + }); + + describe('verifyTextStyles', () => { + it('should not give warning on undefined', () => { + const mixedText = [undefined]; + verifyTextStyles(mixedText, textStyles, defaultStyles); + expect(warnings.length).toBe(0); + }); + + it('should not give warning on null', () => { + const mixedText = [null]; + verifyTextStyles(mixedText, textStyles, defaultStyles); + expect(warnings.length).toBe(0); + }); + + it('should not give warning on ""', () => { + const mixedText = ['']; + verifyTextStyles(mixedText, textStyles, defaultStyles); + expect(warnings.length).toBe(0); + }); + + it('should not give warning on "test"', () => { + const mixedText = ['test']; + verifyTextStyles(mixedText, textStyles, defaultStyles); + expect(warnings.length).toBe(0); + }); + + it('should not give warning on defined styles', () => { + const mixedText = [ + 'test', + { + styleName: 'style1', + mixedText: [ + 'test2', + { + styleName: 'b', + mixedText: ['test3'] + } + ] + } + ]; + verifyTextStyles(mixedText, textStyles, defaultStyles); + expect(warnings.length).toBe(0); + }); + + it('should not give warning on undefined styles', () => { + const mixedText = [ + 'test', + { + styleName: 'style3', + mixedText: [ + 'test2', + { + styleName: 'e', + mixedText: ['test3'] + } + ] + } + ]; + verifyTextStyles(mixedText, textStyles, defaultStyles); + expect(warnings.length).toBe(2); + expect(warnings[0]).toContain('"style3"'); + expect(warnings[1]).toContain('"e"'); + }); + }); +});