Commit 72445332 authored by Tasso Evangelista's avatar Tasso Evangelista Committed by Guilherme Gazzo
Browse files

feat: @rocket.chat/fuselage-hooks package (#65)

parent 30f0804d
{
"New component index.js": {
"scope": "javascript",
"prefix": "fuselage-index",
"body": [
"import React from 'react';",
"",
"import { useStyle } from '../../hooks/styles';",
"import styles from './styles.scss';",
"",
"",
"export function $1({",
" className,",
" ...props",
"}) {",
" const $3ClassName = useStyle(styles, '$2', {}, className);",
" return <div className={$3ClassName} {...props} />;",
"}"
],
},
"New component spec.js": {
"scope": "javascript",
"prefix": "fuselage-spec",
......
......@@ -4,6 +4,10 @@
"directory": "packages/fuselage",
"changeProcessCWD": true
},
{
"directory": "packages/fuselage-hooks",
"changeProcessCWD": true
},
{
"directory": "packages/icons",
"changeProcessCWD": true
......
const path = require('path');
module.exports = {
extends: ['@rocket.chat/eslint-config'],
plugins: ['react'],
parser: 'babel-eslint',
rules: {
indent: ['error', 2],
'import/order': ['error', {
'newlines-between': 'always',
groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index']]
}],
'jsx-quotes': ['error', 'prefer-single'],
'react/jsx-uses-react': 'error',
'react/jsx-uses-vars': 'error',
'react/jsx-no-undef': 'error',
'react/jsx-fragments': ['error', 'syntax'],
},
settings: {
react: {
version: 'detect',
},
},
'env': {
'jest': true,
},
};
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
export const testHook = (callback, ...acts) => {
let returnedValue;
let errorThrown;
class ErrorBoundary extends React.Component {
state = { errored: false }
static getDerivedStateFromError = () => ({ errored: true })
componentDidCatch = (error) => {
errorThrown = error;
}
render = () => (this.state.errored ? null : <>{this.props.children}</>)
}
function TestComponent() {
returnedValue = callback();
return null;
}
const spy = jest.spyOn(console, 'error');
spy.mockImplementation(() => {});
const div = document.createElement('div');
ReactDOM.render(<ErrorBoundary>
<TestComponent />
</ErrorBoundary>, div);
acts.forEach((fn) => act(fn.bind(null, returnedValue)));
ReactDOM.unmountComponentAtNode(div);
if (errorThrown) {
throw errorThrown;
}
return returnedValue;
};
{
"src/**/*.js": [
"eslint --fix",
"git add"
]
}
import { Meta } from '@storybook/addon-docs/blocks';
# Welcome to @rocket.chat/fuselage-hooks 👋
<Meta title='Misc|Hooks' />
[![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](https://github.com/RocketChat/Rocket.Chat.Fuselage#readme)
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/RocketChat/Rocket.Chat.Fuselage/graphs/commit-activity)
[![License: MIT](https://img.shields.io/github/license/RocketChat/@rocket.chat/fuselage-hooks)](https://github.com/RocketChat/Rocket.Chat.Fuselage/blob/master/LICENSE)
[![Twitter: RocketChat](https://img.shields.io/twitter/follow/RocketChat.svg?style=social)](https://twitter.com/RocketChat)
# Hooks
> React Hooks for Fuselage, Rocket.Chat's design system
### 🏠 [Homepage](https://rocket.chat/Rocket.Chat.Fuselage)
## Install
```sh
yarn add @rocket.chat/fuselage-hooks
```
## Run tests
```sh
yarn test
```
## API Reference
......@@ -22,10 +39,12 @@ import { Meta } from '@storybook/addon-docs/blocks';
- [Parameters](#parameters-4)
- [useExclusiveBooleanProps](#useexclusivebooleanprops)
- [Parameters](#parameters-5)
- [useMergedRefs](#usemergedrefs)
- [useMediaQuery](#usemediaquery)
- [Parameters](#parameters-6)
- [useToggle](#usetoggle)
- [useMergedRefs](#usemergedrefs)
- [Parameters](#parameters-7)
- [useToggle](#usetoggle)
- [Parameters](#parameters-8)
### useClassName
......@@ -101,6 +120,16 @@ to choose styling variants.
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** if two or more booleans props are set as true
### useMediaQuery
Hook to listen to a media query.
#### Parameters
- `query` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?** the CSS3 media query expression
Returns **bool** `true` if the media query matches; `false` is it does not match or the query is not defined
### useMergedRefs
Hook to merge refs and callbacks refs into a single callback ref. Useful when your component need a internal ref
......@@ -121,3 +150,24 @@ Hook to create a toggleable boolean state.
- `initialValue` **(any | [function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function))** the initial value or the initial state generator function
Returns **\[any, [function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function)]** a state boolean value and a state toggler function
## Author
👤 **Rocket.Chat**
- Twitter: [@RocketChat](https://twitter.com/RocketChat)
- Github: [@RocketChat](https://github.com/RocketChat)
## 🤝 Contributing
Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/RocketChat/Rocket.Chat.Fuselage/issues).
## Show your support
Give a ⭐️ if this project helped you!
* * *
_This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_
module.exports = {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-class-properties',
],
};
{
"name": "@rocket.chat/fuselage-hooks",
"version": "0.2.0-alpha.6",
"description": "React Hooks for Fuselage, Rocket.Chat's design system",
"homepage": "https://rocket.chat/Rocket.Chat.Fuselage",
"author": {
"name": "Rocket.Chat",
"url": "https://rocket.chat/"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/RocketChat/Rocket.Chat.Fuselage.git"
},
"bugs": {
"url": "https://github.com/RocketChat/Rocket.Chat.Fuselage/issues"
},
"keywords": [
"react",
"hooks",
"fuselage",
"rocketchat"
],
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"start": "rollup -c -w",
"build": "rollup -c",
"test": "jest",
"lint": "eslint src",
"lint-staged": "lint-staged",
"docs": "documentation readme src/index.js --section='API Reference' --readme-file README.md"
},
"devDependencies": {
"@babel/core": "^7.6.4",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/preset-env": "^7.6.3",
"@rocket.chat/eslint-config": "^0.4.0",
"babel-eslint": "^10.0.3",
"documentation": "^12.1.2",
"eslint": "^6.5.1",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-react": "^7.16.0",
"invariant": "^2.2.4",
"jest": "^24.9.0",
"lint-staged": "^9.4.2",
"react": "^16.10.2",
"rollup": "^1.23.1",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-commonjs": "^10.1.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-peer-deps-external": "^2.2.0"
},
"peerDependencies": {
"invariant": "^2.2.4",
"react": "^16.10.2"
}
}
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import external from 'rollup-plugin-peer-deps-external';
import resolve from 'rollup-plugin-node-resolve';
import pkg from './package.json';
export default {
input: 'src/index.js',
output: [
{
file: pkg.main,
format: 'es',
sourcemap: true
}
],
plugins: [
external(),
babel(),
resolve(),
commonjs(),
],
};
export const fromCamelToKebabCase = (camelCaseString) =>
camelCaseString.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
export const debounce = (fn, delay) => {
let timer;
let callback;
function f(...args) {
const context = this;
clearTimeout(timer);
callback = () => fn.apply(context, args);
timer = setTimeout(callback, delay);
return context;
}
f.flush = () => {
clearTimeout(timer);
callback();
};
f.cancel = () => clearTimeout(timer);
return f;
};
......@@ -2,6 +2,7 @@ export * from './useClassName';
export * from './useDebouncedUpdates';
export * from './useDebouncedCallback';
export * from './useExclusiveBooleanProps';
export * from './useMediaQuery';
export * from './useMergedRefs';
export * from './useToggle';
export * from './useUniqueId';
import { useMemo } from 'react';
import { fromCamelToKebabCase } from '../helpers';
import { fromCamelToKebabCase } from './helpers';
/**
* Hook to generate a BEM-compatible `className` value for a component.
......
import { useMemo } from 'react';
import { debounce } from '../helpers';
import { debounce } from './helpers';
/**
* Hook to memoize a debounced version of a callback.
......
import { useLayoutEffect, useState } from 'react';
/**
* Hook to listen to a media query.
*
* @param {string} [query] - the CSS3 media query expression
* @return {bool} - `true` if the media query matches; `false` is it does not match or the query is not defined
*/
export const useMediaQuery = (query) => {
const [matches, setMatches] = useState(() => {
if (!query) {
return false;
}
const { matches } = window.matchMedia(query);
return !!matches;
});
useLayoutEffect(() => {
if (!query) {
return;
}
let mounted = true;
const mql = window.matchMedia(query);
const handleChange = () => {
if (!mounted) {
return;
}
setMatches(!!mql.matches);
};
mql.addListener(handleChange);
setMatches(mql.matches);
return () => {
mounted = false;
mql.removeListener(handleChange);
};
}, [query]);
return matches;
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment