From 5fcbcef4b4d30aef4288ee07007c12df4595b710 Mon Sep 17 00:00:00 2001 From: Devin Fee Date: Thu, 13 Jun 2019 00:38:05 -0700 Subject: [PATCH] first hack at extensions --- package-lock.json | 33 +++- package.json | 6 +- src/extensions/badge/__docs__/badge.docs.mdx | 162 ++++++++++++++++++ src/extensions/badge/__tests__/badge.test.tsx | 89 ++++++++++ src/extensions/badge/badge.tsx | 65 +++++++ src/extensions/badge/index.ts | 1 + .../divider/__docs__/divider.docs.mdx | 66 +++++++ .../divider/__tests__/divider.test.tsx | 77 +++++++++ src/extensions/divider/divider.tsx | 44 +++++ src/extensions/divider/index.ts | 1 + src/extensions/index.ts | 3 + .../page-loader/__docs__/page-loader.docs.mdx | 119 +++++++++++++ .../__docs__/simple-page-loader-manager.tsx | 41 +++++ .../__tests__/page-loader.test.tsx | 75 ++++++++ src/extensions/page-loader/index.ts | 1 + src/extensions/page-loader/page-loader.tsx | 46 +++++ .../tooltip/__docs__/tooltip.docs.mdx | 62 +++++++ .../tooltip/__tests__/badge.test.tsx | 89 ++++++++++ src/extensions/tooltip/index.ts | 1 + src/extensions/tooltip/tooltip.tsx | 80 +++++++++ src/index.sass | 5 + 21 files changed, 1063 insertions(+), 3 deletions(-) create mode 100644 src/extensions/badge/__docs__/badge.docs.mdx create mode 100644 src/extensions/badge/__tests__/badge.test.tsx create mode 100644 src/extensions/badge/badge.tsx create mode 100644 src/extensions/badge/index.ts create mode 100644 src/extensions/divider/__docs__/divider.docs.mdx create mode 100644 src/extensions/divider/__tests__/divider.test.tsx create mode 100644 src/extensions/divider/divider.tsx create mode 100644 src/extensions/divider/index.ts create mode 100644 src/extensions/index.ts create mode 100644 src/extensions/page-loader/__docs__/page-loader.docs.mdx create mode 100644 src/extensions/page-loader/__docs__/simple-page-loader-manager.tsx create mode 100644 src/extensions/page-loader/__tests__/page-loader.test.tsx create mode 100644 src/extensions/page-loader/index.ts create mode 100644 src/extensions/page-loader/page-loader.tsx create mode 100644 src/extensions/tooltip/__docs__/tooltip.docs.mdx create mode 100644 src/extensions/tooltip/__tests__/badge.test.tsx create mode 100644 src/extensions/tooltip/index.ts create mode 100644 src/extensions/tooltip/tooltip.tsx diff --git a/package-lock.json b/package-lock.json index fde68b1a..c0bdcbd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3462,8 +3462,37 @@ "bulma": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz", - "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==", - "dev": true + "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==" + }, + "bulma-badge": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bulma-badge/-/bulma-badge-3.0.1.tgz", + "integrity": "sha512-owRTbInXkpsSrFSvCXyWMNyjcQXr7j3Vj4aSnKYISalcmYqql/YVC7e82M/MXFQG8J66ak5VvIu6dCrdAiQUtQ==", + "requires": { + "bulma": "^0.7.5" + }, + "dependencies": { + "bulma": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz", + "integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw==" + } + } + }, + "bulma-divider": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/bulma-divider/-/bulma-divider-0.2.0.tgz", + "integrity": "sha512-REe3k56GECRfDaqFjC8cwLhV4RxXmV0RubuzDJqwior9wlJcdHlN0qfW0tvUX+qphikaTQegIeRuhjRIAqkjkw==" + }, + "bulma-pageloader": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bulma-pageloader/-/bulma-pageloader-2.1.0.tgz", + "integrity": "sha512-yI1SBxeGfj57BYtN8tIcvaGlfQgxylPwHHaDNSYLhF+1EVwUjWz97Ufga+8VOv9PZuhhnD05rb8nZ+p0isWw9A==" + }, + "bulma-tooltip": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bulma-tooltip/-/bulma-tooltip-2.0.2.tgz", + "integrity": "sha512-xsqWeWV7tsUn3uH04SqJeP7/CyC1RaDVIyVzr4/sIO3friIIOi7L6jc5g7qUwDxuBQl72yH/yRPuefpXoQ4hWg==" }, "bytes": { "version": "3.1.0", diff --git a/package.json b/package.json index f74c6bda..4266649c 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,11 @@ }, "homepage": "https://github.com/dfee/rbx#readme", "dependencies": { + "bulma": "0.7.5", + "bulma-badge": "^3.0.1", + "bulma-divider": "^0.2.0", + "bulma-pageloader": "^2.1.0", + "bulma-tooltip": "^2.0.2", "classnames": "^2.2.6", "prop-types": "^15.7.2" }, @@ -57,7 +62,6 @@ "@types/react": "^16.8.19", "@types/react-dom": "^16.8.4", "@types/react-test-renderer": "^16.8.1", - "bulma": "0.7.5", "coveralls": "3.0.4", "css-loader": "^2.1.1", "docz": "^1.2.0", diff --git a/src/extensions/badge/__docs__/badge.docs.mdx b/src/extensions/badge/__docs__/badge.docs.mdx new file mode 100644 index 00000000..eba3c1fb --- /dev/null +++ b/src/extensions/badge/__docs__/badge.docs.mdx @@ -0,0 +1,162 @@ +--- +name: Badge +menu: Extensions +route: /extensions/badge +--- + +import { Playground } from "docz"; + +import { + ForwardRefAsExoticComponentDoc, + mapEnumerable, + OptionBlock, +} from "src/__docs__/components"; +import { DEFAULTS } from "src/base/helpers/variables"; +import { Block, Button, Title } from "src/elements"; +import { Badge, BADGE_DEFAULTS } from "../badge"; + +# Badge + +Display a **badge** element in front of another component. + +By default, it renders as a `span`. +It can display number or strings. + + + + Notifications + + + +But you can use the `as` prop to render any component or JSX element, like a `} + > + PageLoader + + + + + + Note: the {""} used in + these playgrounds is a reference implementation for the docs, and is not + available in the release. + rbx provides a foundation, not an opinion for + components. + + +### Colors + +Use the `color` prop on `` to specify the color. + + + + + {DEFAULTS.colors.map((color, i) => ( + Toggle {color}} + > + {color} PageLoader + + ))} + + + + +### Direction + +Use the `direction` prop on `` to specify the loading direction. + + + + {() => { + const directions = [ + { name: 'top-to-bottom (default)', direction: undefined}, + ...PAGE_LOADER_DEFAULTS.directions.map(direction => ({ + direction, name: direction + })) + ]; + + return ( + + {directions.map(({ direction, name }, i) => ( + Toggle {name}} + > + {name} PageLoader + + ))} + + ); + }} + + + + +## API + + diff --git a/src/extensions/page-loader/__docs__/simple-page-loader-manager.tsx b/src/extensions/page-loader/__docs__/simple-page-loader-manager.tsx new file mode 100644 index 00000000..c58b00be --- /dev/null +++ b/src/extensions/page-loader/__docs__/simple-page-loader-manager.tsx @@ -0,0 +1,41 @@ +import React from "react"; + +import { Button } from "src/elements/button/button"; +import { Omit } from "src/types"; +import { PageLoader, PageLoaderProps } from "../page-loader"; + +export type SimplePageLoaderManagerProps = Omit & { + children: React.ReactNode; + button: React.ReactElement>; +}; +export type SimplePageLoaderManagerState = { active: boolean }; + +export class SimplePageLoaderManager extends React.Component< + SimplePageLoaderManagerProps, + SimplePageLoaderManagerState +> { + public readonly state: SimplePageLoaderManagerState = { active: false }; + + public render() { + const { button, ...rest } = this.props; + + const managedButton = React.cloneElement(button, { + onClick: this.handleClick, + }); + + return ( + + {managedButton} + + + ); + } + + private readonly handleClick = () => { + this.setState({ active: true }, () => { + setTimeout(() => { + this.setState({ active: false }); + }, 3000); + }); + }; +} diff --git a/src/extensions/page-loader/__tests__/page-loader.test.tsx b/src/extensions/page-loader/__tests__/page-loader.test.tsx new file mode 100644 index 00000000..a89db4d6 --- /dev/null +++ b/src/extensions/page-loader/__tests__/page-loader.test.tsx @@ -0,0 +1,75 @@ +import React from "react"; + +import { DEFAULTS } from "src/base/helpers/variables"; +import { + PAGE_LOADER_DEFAULTS, + PageLoader, +} from "src/extensions/page-loader/page-loader"; + +import { + hasProperties, + makeGenericHOCShallowWrapperInContextConsumer, + testForwardRefAsExoticComponentIntegration, + testThemeIntegration, + validateBoolPropType, + validateStringOrNumberPropType, +} from "src/__tests__/testing"; + +const COMPONENT = PageLoader; +const DISPLAY_NAME = "PageLoader"; +const DEFAULT_ELEMENT = "div"; +const BULMA_CLASS_NAME = "pageloader"; + +describe(`${DISPLAY_NAME} component`, () => { + hasProperties(COMPONENT, { + defaultProps: { as: DEFAULT_ELEMENT }, + }); + + testForwardRefAsExoticComponentIntegration(COMPONENT, { + displayName: DISPLAY_NAME, + bulmaClassName: BULMA_CLASS_NAME, + defaultElement: DEFAULT_ELEMENT, + }); + + testThemeIntegration(COMPONENT); + + describe("props", () => { + const { propTypes } = COMPONENT; + + describe("active", () => { + validateBoolPropType(propTypes, "active"); + + [false, true].map(active => { + it(`should ${active ? "" : "not "}be active`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect(wrapper.hasClass("is-active")).toBe(active); + }); + }); + }); + + describe("color", () => { + validateStringOrNumberPropType(propTypes, "color"); + + DEFAULTS.colors.map(color => { + it(`should be ${color}`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect(wrapper.hasClass(`is-${color}`)).toBe(true); + }); + }); + }); + + describe("direction", () => { + validateStringOrNumberPropType(propTypes, "direction"); + + PAGE_LOADER_DEFAULTS.directions.map(direction => { + it(`should be ${direction}`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect(wrapper.hasClass(`is-${direction}`)).toBe(true); + }); + }); + }); + }); +}); diff --git a/src/extensions/page-loader/index.ts b/src/extensions/page-loader/index.ts new file mode 100644 index 00000000..6ebc72cb --- /dev/null +++ b/src/extensions/page-loader/index.ts @@ -0,0 +1 @@ +export { PageLoader } from "./page-loader"; diff --git a/src/extensions/page-loader/page-loader.tsx b/src/extensions/page-loader/page-loader.tsx new file mode 100644 index 00000000..61da57e7 --- /dev/null +++ b/src/extensions/page-loader/page-loader.tsx @@ -0,0 +1,46 @@ +import classNames from "classnames"; +import PropTypes from "prop-types"; +import React from "react"; + +import { forwardRefAs, Generic } from "../../base"; +import { HelpersProps } from "../../base/helpers"; +import { Variables } from "../../base/helpers/variables"; +import { tuple } from "../../utils"; + +export const PAGE_LOADER_DEFAULTS = { + directions: tuple("right-to-left", "left-to-right"), +}; + +export type PageLoaderModifierProps = Partial<{ + active: boolean; + color: Variables["colors"]; + direction: (typeof PAGE_LOADER_DEFAULTS["directions"])[number]; +}>; + +export type PageLoaderProps = HelpersProps & PageLoaderModifierProps; + +export const PageLoader = forwardRefAs( + ({ active, className, color, direction, ...rest }, ref) => ( + + ), + { as: "div" }, +); + +PageLoader.displayName = "PageLoader"; +PageLoader.propTypes = { + active: PropTypes.bool, + color: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + direction: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +}; diff --git a/src/extensions/tooltip/__docs__/tooltip.docs.mdx b/src/extensions/tooltip/__docs__/tooltip.docs.mdx new file mode 100644 index 00000000..67955421 --- /dev/null +++ b/src/extensions/tooltip/__docs__/tooltip.docs.mdx @@ -0,0 +1,62 @@ +--- +name: Tooltip +menu: Extensions +route: /extensions/tooltip +--- + +import { Playground } from "docz"; + +import { + ForwardRefAsExoticComponentDoc, + mapEnumerable, + OptionBlock, +} from "src/__docs__/components"; +import { DEFAULTS } from "src/base/helpers/variables"; +import { Block, Button, Title } from "src/elements"; +import { Tooltip, TOOLTIP_DEFAULTS } from "../tooltip"; + +# Tooltip + +Display a **tooltip** attached to any kind of element with different positioning. + +It can display number or strings. + + + + + +## API + + diff --git a/src/extensions/tooltip/__tests__/badge.test.tsx b/src/extensions/tooltip/__tests__/badge.test.tsx new file mode 100644 index 00000000..07e568c7 --- /dev/null +++ b/src/extensions/tooltip/__tests__/badge.test.tsx @@ -0,0 +1,89 @@ +import React from "react"; + +import { DEFAULTS } from "src/base/helpers/variables"; +import { Badge, BADGE_DEFAULTS } from "src/extensions/badge/badge"; + +import { + hasProperties, + makeGenericHOCShallowWrapperInContextConsumer, + testForwardRefAsExoticComponentIntegration, + testThemeIntegration, + validateBoolPropType, + validateStringOrNumberPropType, +} from "src/__tests__/testing"; + +const COMPONENT = Badge; +const DISPLAY_NAME = "Badge"; +const DEFAULT_ELEMENT = "span"; +const BULMA_CLASS_NAME = "badge"; + +describe(`${DISPLAY_NAME} component`, () => { + hasProperties(COMPONENT, { + defaultProps: { + as: DEFAULT_ELEMENT, + badgeContent: "", + }, + }); + + testForwardRefAsExoticComponentIntegration(COMPONENT, { + displayName: DISPLAY_NAME, + bulmaClassName: BULMA_CLASS_NAME, + defaultElement: DEFAULT_ELEMENT, + }); + + testThemeIntegration(COMPONENT); + + describe("props", () => { + const { propTypes } = COMPONENT; + + describe("badgeColor", () => { + validateStringOrNumberPropType(propTypes, "badgeColor"); + + DEFAULTS.colors.map(color => { + it(`should be ${color}`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect(wrapper.hasClass(`has-badge-${color}`)).toBe(true); + }); + }); + }); + + describe("badgeContent", () => { + validateStringOrNumberPropType(propTypes, "badgeContent"); + + it(`should have proper content`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect( + (wrapper.props() as React.HTMLAttributes)[ + "data-badge" + ], + ).toBe("foo"); + }); + }); + + describe("badgeOutlined", () => { + validateBoolPropType(propTypes, "badgeOutlined"); + + [false, true].map(outlined => { + it(`should ${outlined ? "" : "not "}be outlined`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect(wrapper.hasClass(`has-badge-outlined`)).toBe(outlined); + }); + }); + }); + + describe("badgeSize", () => { + validateStringOrNumberPropType(propTypes, "badgeSize"); + + BADGE_DEFAULTS.sizes.map(size => { + it(`should be ${size}`, () => { + const node = ; + const wrapper = makeGenericHOCShallowWrapperInContextConsumer(node); + expect(wrapper.hasClass(`has-badge-${size}`)).toBe(true); + }); + }); + }); + }); +}); diff --git a/src/extensions/tooltip/index.ts b/src/extensions/tooltip/index.ts new file mode 100644 index 00000000..54cbb070 --- /dev/null +++ b/src/extensions/tooltip/index.ts @@ -0,0 +1 @@ +export { Badge } from "./tooltip"; diff --git a/src/extensions/tooltip/tooltip.tsx b/src/extensions/tooltip/tooltip.tsx new file mode 100644 index 00000000..d5896844 --- /dev/null +++ b/src/extensions/tooltip/tooltip.tsx @@ -0,0 +1,80 @@ +import classNames from "classnames"; +import PropTypes from "prop-types"; +import React from "react"; + +import { forwardRefAs, Generic } from "../../base"; +import { HelpersProps } from "../../base/helpers"; +import { DEFAULTS, Variables } from "../../base/helpers/variables"; + +export const TOOLTIP_DEFAULTS = { + positions: ["top", "right", "bottom", "left"], +} as const; + +export type TooltipModifierProps = Partial<{ + tooltipActive: boolean; + tooltipColor: Variables["colors"]; + tooltipContent: number | string; + tooltipMultiline: boolean; + tooltipPosition: (typeof TOOLTIP_DEFAULTS["positions"])[number]; + tooltipResponsive: { + [K in Variables["breakpoints"]]?: (typeof TOOLTIP_DEFAULTS["positions"])[number]; + }; +}>; + +export type TooltipProps = HelpersProps & TooltipModifierProps; + +export const Tooltip = forwardRefAs( + ( + { + className, + tooltipActive, + tooltipColor, + tooltipContent, + tooltipMultiline, + tooltipPosition, + tooltipResponsive = {}, + ...rest + }, + ref, + ) => ( + + `is-tooltip-${tooltipResponsive[breakpoint]}-${breakpoint}`, + ), + className, + )} + data-tooltip={tooltipContent} + ref={ref} + {...rest} + /> + ), + { + as: "span", + tooltipContent: "", + }, +); + +Tooltip.displayName = "Tooltip"; +Tooltip.propTypes = { + tooltipActive: PropTypes.bool, + tooltipColor: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + tooltipMultiline: PropTypes.bool, + tooltipPosition: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + tooltipResponsive: PropTypes.shape( + DEFAULTS.breakpoints + .map(breakpoint => ({ + [breakpoint]: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + })) + .reduce((acc, cv) => ({ ...acc, ...cv }), {}), + ), +}; diff --git a/src/index.sass b/src/index.sass index 74806b46..ad6f49a7 100644 --- a/src/index.sass +++ b/src/index.sass @@ -1,2 +1,7 @@ @import "~bulma/bulma.sass" @import "./rbx.sass" + +@import "~bulma-badge/dist/css/bulma-badge.sass" +@import "~bulma-divider/dist/css/bulma-divider.sass" +@import "~bulma-pageloader/dist/css/bulma-pageloader.sass" +@import "~bulma-tooltip/dist/css/bulma-tooltip.sass"