├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── blank.yml
│ └── publish.yml
├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── jest.config.js
├── lib
└── index.tsx
├── package-lock.json
├── package.json
├── scripts
└── setupEnzyme.ts
├── tests
└── index.test.tsx
└── tsconfig.json
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/blank.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | test:
10 | name: Run package tests
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v1
15 | - uses: actions/setup-node@v1
16 | - name: npm install, build and test
17 | env:
18 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
19 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
20 | run: |
21 | npm install
22 | npm run build
23 | npm run coveralls
24 |
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish
2 |
3 | on:
4 | create:
5 | types: [tag]
6 |
7 | jobs:
8 | test:
9 | name: Run package tests
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v1
14 | - uses: actions/setup-node@v1
15 | - name: npm install, build and test
16 | env:
17 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
18 | run: |
19 | npm install
20 | npm run build
21 | npm t
22 | - name: Publish package
23 | env:
24 | NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
25 | run: npm publish
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.DS_Store
2 | /dist
3 | /.rts2_cache_cjs/
4 | /node_modules
5 | /coverage
6 | .rts2_cache_es/
7 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | //registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Felix Wostal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://badge.fury.io/js/react-robohash)
4 | [](https://github.com/fel1xw/react-robohash/actions)
5 | [](https://npm.im/react-robohash)
6 | [](https://coveralls.io/github/fel1xw/react-robohash?branch=master)
7 | [](https://opensource.org/licenses/MIT)
8 | [](https://unpkg.com/react-robohash@latest/dist/index.js)
9 | [](https://snyk.io/test/github/fel1xw/react-robohash)
10 |
11 | ## Install
12 | ```sh
13 | npm install react-robohash --save
14 | # or
15 | yarn add react-robohash
16 | ```
17 |
18 | ## Playground
19 | You can play around with the module on [codesandbox](https://codesandbox.io/s/zrpz3jpzp?fontsize=14)
20 |
21 | ## Props
22 | | Name | Type | Required | Default | Description |
23 | |-------------|-----------------|----------|--------------------|-----------------------------------------------------------------------------|
24 | | name | string | true | | name, email, whatever identifier you want |
25 | | alt | string | false | `Robohash-${name}` | alt property for image |
26 | | className | string | false | Robohash | pass custom class |
27 | | size | string | number | false | | specify image size like 200 or 100x50 (square works best) |
28 | | type | string | number | false | robot | type - "robot", "alien", "head", "cat", 1, 2, 3, 4 |
29 | | background | string | false | 0 | custom background set (1, 2) |
30 | | fileType | string | false | png | image return type, png, svg, jpg (see homepage) |
31 | | gravatar | boolean | false | false | checks if gravatar is available otherwise fallbacks to robot |
32 | | children | function | false | | custom render prop function which gets only the url passed in (see example) |
33 |
34 | ## Usage
35 | ```
36 | import Robohash from 'react-robohash';
37 |
38 |
42 | ```
43 |
44 | or with custom element as render prop function
45 | ```
46 | import Robohash from 'react-robohash';
47 |
48 |
49 | {url => {url}}
50 |
51 | ```
52 |
53 | ## Author
54 | * Felix Wostal [@felixwostal](https://twitter.com/felixwostal)
55 |
56 | ## Support
57 | If you like the project and want to support my work, you can think about buy me a coffee :)
58 |
59 | [](https://paypal.me/felixwostal/1)
60 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "roots": [
3 | ""
4 | ],
5 | "transform": {
6 | "^.+\\.tsx?$": "ts-jest"
7 | },
8 | "testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.tsx?$",
9 | "moduleFileExtensions": [
10 | "ts",
11 | "tsx",
12 | "js",
13 | "jsx",
14 | ],
15 | "setupFiles": ["/scripts/setupEnzyme.ts"]
16 | }
17 |
--------------------------------------------------------------------------------
/lib/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | type Props = {
4 | name: string,
5 | alt?: string,
6 | className?: string,
7 | size?: string | number,
8 | type?: string | number,
9 | fileType?: string,
10 | background?: number,
11 | gravatar?: boolean,
12 | children?: (url: string) => React.ReactElement
13 | }
14 |
15 | export const generateURL = ({ name, fileType = 'png', ...rest }: Props) => {
16 | const url = new URL(`https://robohash.org/${name}.${fileType}`)
17 | Object.entries(rest)
18 | .map(transformKeyAndValue)
19 | .forEach(([ key, value ]) => {
20 | url.searchParams.set(key, value)
21 | })
22 |
23 | return url.href
24 | }
25 |
26 | const getSetForType = (type: string) => {
27 | switch (type.toLowerCase()) {
28 | case 'robot':
29 | return 'set1'
30 | case 'alien':
31 | return 'set2'
32 | case 'head':
33 | return 'set3'
34 | case 'cat':
35 | return 'set4'
36 | default:
37 | return 'set1'
38 | }
39 | }
40 | const transformKeyAndValue = ([ key, value ]: any[]) => {
41 | switch (key) {
42 | case 'type': {
43 | const val = typeof value === 'number' ? `set${value}` : getSetForType(value)
44 | return ['set', val]
45 | }
46 | case 'background':
47 | return ['bgset', `bg${value}`]
48 | case 'gravatar':
49 | return value ? ['gravatar', 'yes'] : []
50 | case 'size':
51 | const val = typeof value === 'number' ? `${value}x${value}` : value
52 | return ['size', val]
53 | default:
54 | return []
55 | }
56 | }
57 |
58 | const ReactRoboHash = ({ alt, children, className = 'Robohash', ...rest }: Props & React.HTMLAttributes): React.ReactElement => {
59 | const url = generateURL(rest)
60 |
61 | return children
62 | ? children(url)
63 | :
64 | }
65 |
66 | export default ReactRoboHash;
67 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-robohash",
3 | "version": "1.0.9",
4 | "description": "A react component for robohash.org",
5 | "files": [
6 | "dist"
7 | ],
8 | "types": "dist/index.d.ts",
9 | "main": "dist/index.js",
10 | "scripts": {
11 | "prebuild": "rimraf dist",
12 | "build": "tsc",
13 | "dev": "tsc --watch",
14 | "test": "jest",
15 | "coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls",
16 | "prepublishOnly": "npm t && npm run build"
17 | },
18 | "keywords": [
19 | "react",
20 | "robohash",
21 | "react-robohash"
22 | ],
23 | "author": {
24 | "name": "Felix Wostal",
25 | "url": "https://felixwostal.de"
26 | },
27 | "bugs": {
28 | "url": "https://github.com/fel1xw/react-robohash/issues"
29 | },
30 | "homepage": "https://github.com/fel1xw/react-robohash#readme",
31 | "license": "MIT",
32 | "devDependencies": {
33 | "@types/enzyme": "^3.9.0",
34 | "@types/enzyme-adapter-react-16": "^1.0.5",
35 | "@types/jest": "^24.0.6",
36 | "@types/node": "^11.9.5",
37 | "@types/react": "^16.8.5",
38 | "@types/react-dom": "^16.8.2",
39 | "coveralls": "^3.0.3",
40 | "enzyme": "^3.9.0",
41 | "enzyme-adapter-react-16": "^1.10.0",
42 | "jest": "^24.1.0",
43 | "react": "^16.8.3",
44 | "react-dom": "^16.8.3",
45 | "rimraf": "^2.6.3",
46 | "ts-jest": "^24.0.0",
47 | "typescript": "^3.3.3333"
48 | },
49 | "peerDependencies": {
50 | "react": "^16.8.3",
51 | "react-dom": "^16.8.3"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/scripts/setupEnzyme.ts:
--------------------------------------------------------------------------------
1 | import Enzyme from 'enzyme'
2 | import Adapter from 'enzyme-adapter-react-16'
3 |
4 | Enzyme.configure({ adapter: new Adapter() })
5 |
--------------------------------------------------------------------------------
/tests/index.test.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import { shallow } from 'enzyme'
3 | import { parse } from 'url'
4 |
5 | import Robohash from '../lib'
6 |
7 | const baseProps = {
8 | name: 'react-robohash',
9 | alt: 'custom-alt',
10 | className: 'custom-class',
11 | size: 200,
12 | type: 'robot',
13 | fileType: 'png',
14 | background: 0,
15 | gravatar: true,
16 | }
17 |
18 | const parseToQueryObject = (query: string) => JSON.parse('{"' + decodeURI(query.replace(/&/g, "\",\"").replace(/=/g, "\":\"")) + '"}')
19 |
20 | describe('', () => {
21 | it('renders with baseProps', () => {
22 | const wrapper = shallow()
23 | const img = wrapper.find('img')
24 | expect(img).toBeTruthy()
25 | expect(img.prop('src')).toContain('.png')
26 | expect(img.prop('alt')).toBe(baseProps.alt)
27 | expect(img.hasClass(baseProps.className)).toBe(true)
28 | })
29 |
30 | it('render with function as children', () => {
31 | const props = {
32 | name: 'robo',
33 | children: (url: string) => {url}
34 | }
35 | const wrapper = shallow()
36 | expect(wrapper.text().length).toBeGreaterThan(0)
37 | })
38 |
39 | it('properly formats the url', () => {
40 | const wrapper = shallow()
41 | const query = parseToQueryObject(parse(wrapper.find('img').prop('src')!).query!)
42 | expect(query.bgset).toBe('bg0')
43 | expect(query.gravatar).toBe('yes')
44 | expect(query.set).toBe('set1')
45 | expect(query.size).toBe('200x200')
46 | })
47 |
48 | it('accepts size as string', () => {
49 | const wrapper = shallow()
50 | const query = parseToQueryObject(parse(wrapper.find('img').prop('src')!).query!)
51 | expect(query.size).toBe('100x100')
52 | })
53 |
54 | it('passes no when gravatar is false', () => {
55 | const wrapper = shallow()
56 | const query = parseToQueryObject(parse(wrapper.find('img').prop('src')!).query!)
57 | expect(query.gravatar).not.toBeDefined()
58 | })
59 |
60 | it('has default class and alt value', () => {
61 | const wrapper = shallow()
62 | expect(wrapper.find('img').hasClass('Robohash')).toBe(true)
63 | expect(wrapper.find('img').prop('alt')).toBe('Robohash-robo')
64 | })
65 |
66 | it('ignores custom props in query', () => {
67 | const wrapper = shallow()
68 | expect(wrapper.find('img').prop('src')).not.toContain('onClick')
69 | })
70 |
71 | it('accepts string or number for type', () => {
72 | const cases = [
73 | ['robot', 'set1'],
74 | ['alien', 'set2'],
75 | ['head', 'set3'],
76 | ['cat', 'set4'],
77 | ['xyz', 'set1'],
78 | [1, 'set1'],
79 | [2, 'set2'],
80 | [3, 'set3'],
81 | [4, 'set4'],
82 | ]
83 |
84 | cases.forEach(([ type, expected ]) => {
85 | const wrapper = shallow()
86 | expect(wrapper.find('img').prop('src')).toContain(expected)
87 | })
88 | })
89 | })
90 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | "lib": [
7 | "dom",
8 | "es6",
9 | "es7",
10 | "es2017"
11 | ], /* Specify library files to be included in the compilation. */
12 | // "allowJs": true, /* Allow javascript files to be compiled. */
13 | // "checkJs": true, /* Report errors in .js files. */
14 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
15 | "declaration": true, /* Generates corresponding '.d.ts' file. */
16 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
17 | "sourceMap": true, /* Generates corresponding '.map' file. */
18 | // "outFile": "./", /* Concatenate and emit output to single file. */
19 | "outDir": "dist", /* Redirect output structure to the directory. */
20 | "rootDir": "lib", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
21 | // "composite": true, /* Enable project compilation */
22 | "removeComments": true, /* Do not emit comments to output. */
23 | // "noEmit": true, /* Do not emit outputs. */
24 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
25 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
26 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
35 | /* Additional Checks */
36 | "noUnusedLocals": true, /* Report errors on unused locals. */
37 | "noUnusedParameters": true, /* Report errors on unused parameters. */
38 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
39 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
40 | /* Module Resolution Options */
41 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
42 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
43 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
44 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
45 | // "typeRoots": [], /* List of folders to include type definitions from. */
46 | // "types": [], /* Type declaration files to be included in compilation. */
47 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
48 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
49 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
50 | /* Source Map Options */
51 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
52 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
53 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
54 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
55 | /* Experimental Options */
56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
58 | },
59 | "exclude": [
60 | "node_modules",
61 | "**/*.test.",
62 | "dist",
63 | "scripts",
64 | "tests"
65 | ]
66 | }
67 |
--------------------------------------------------------------------------------