From c959e1bc623d624e168f67ffc1068d41c9fc8a33 Mon Sep 17 00:00:00 2001 From: Anurag Date: Fri, 16 Oct 2020 15:30:16 +0530 Subject: [PATCH 1/5] chore(jsdoc): added toast jsdocs --- package.json | 4 ++-- src/segment/SegmentState.ts | 17 +++++++++++++++-- src/toast/ToastController.tsx | 16 ++++++++++++++++ src/toast/ToastProvider.tsx | 20 ++++++++++++++++++++ src/toast/ToastState.ts | 3 +-- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 78cf6bdcb..c19162d33 100644 --- a/package.json +++ b/package.json @@ -117,8 +117,8 @@ "lodash": "4.17.20", "mockdate": "^3.0.2", "prettier": "2.1.2", - "react": "16.13.1", - "react-dom": "16.13.1", + "react": "^16.14.0", + "react-dom": "^16.14.0", "react-hook-form": "6.9.2", "react-spring": "8.0.27", "react-test-renderer": "16.13.1", diff --git a/src/segment/SegmentState.ts b/src/segment/SegmentState.ts index 55c26223b..876df80a5 100644 --- a/src/segment/SegmentState.ts +++ b/src/segment/SegmentState.ts @@ -48,6 +48,20 @@ const TYPE_MAPPING = { export interface SegmentStateProps { value?: Date; defaultValue?: Date; + /** + * Sets formmating of date based on Intl.DateFormatOptions + * + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat + * + * @example + * { + * year: "numeric", + * month: "2-digit", + * day: "2-digit", + * weekday: "long", + * } + * + */ formatOptions?: DateTimeFormatOpts; placeholderDate?: Date; onChange?: (value: Date, ...args: any[]) => void; @@ -124,8 +138,7 @@ export function useSegmentState(props: SegmentStateProps) { ) => { validSegments[type] = true; setValidSegments({ ...validSegments }); - // @ts-ignore - setValue(add(value, type, amount, resolvedOptions)); + setValue(add(value, type, amount, resolvedOptions) as Date); }; return { diff --git a/src/toast/ToastController.tsx b/src/toast/ToastController.tsx index 233333436..1ff7d93cb 100644 --- a/src/toast/ToastController.tsx +++ b/src/toast/ToastController.tsx @@ -3,9 +3,25 @@ import { useTimeout } from "@chakra-ui/hooks"; interface ToastControllerProps { id: string; + /** + * Duration after the toast will be removed automatically if autoDismiss is true + * + * @default 0 + */ duration?: number; + /** + * If True the toast will automatically dismiss after the specified duration + */ autoDismiss?: boolean; + /** + * Dragging threshold at which point the toast will be swiped out + * + * @default 100 + */ dragThreshold?: number; + /** + * Callback will fire after the toast has been removed + */ onRequestRemove: (id: string) => void; } diff --git a/src/toast/ToastProvider.tsx b/src/toast/ToastProvider.tsx index 0216d01cd..d6888cb35 100644 --- a/src/toast/ToastProvider.tsx +++ b/src/toast/ToastProvider.tsx @@ -41,11 +41,31 @@ export type ToastTypes = Record< >; export type TToastWrapper = (props: any) => React.ReactElement; + type IToastProvider = { + /** + * Specify types of toast in an object + */ toastTypes: ToastTypes; + /** + * If True the toast will automatically dismiss after the specified duration + */ autoDismiss?: boolean; + /** + * Timeout after the toast will be removed automatically if autoDismiss is true + * + * @default 5000 + */ timeout?: number; + /** + * Duration of delay after the toast will be unmounted, so that animations can run + * + * @default 0 + */ animationTimeout?: number; + /** + * Callback function to enhance the behaviour of ToastControllers + */ toastWrapper?: TToastWrapper; placement?: Placements; }; diff --git a/src/toast/ToastState.ts b/src/toast/ToastState.ts index a5f8079b2..2a7eae7c1 100644 --- a/src/toast/ToastState.ts +++ b/src/toast/ToastState.ts @@ -3,8 +3,7 @@ import { v4 as uuidv4 } from "uuid"; import { Placements } from "./ToastProvider"; -type JSXFunction = (props: any) => JSX.Element; -type StringOrElement = string | JSXFunction; +type StringOrElement = string | React.ReactNode; export interface IToast { id: string; From e1578abba681222222ba2977f89af5bf40a82eb1 Mon Sep 17 00:00:00 2001 From: Anurag Date: Mon, 19 Oct 2020 18:10:47 +0530 Subject: [PATCH 2/5] chore: jsdoc updates --- src/toast/ToastProvider.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/toast/ToastProvider.tsx b/src/toast/ToastProvider.tsx index d6888cb35..636863bb5 100644 --- a/src/toast/ToastProvider.tsx +++ b/src/toast/ToastProvider.tsx @@ -67,6 +67,9 @@ type IToastProvider = { * Callback function to enhance the behaviour of ToastControllers */ toastWrapper?: TToastWrapper; + /** + * Placement of the toast on the screen + */ placement?: Placements; }; From fdb9dab8becde8e9a408ec4990ec57291b305bc0 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 20 Oct 2020 12:47:09 +0530 Subject: [PATCH 3/5] chore: toast jsdoc and improvements --- src/timepicker/__utils.ts | 2 +- src/toast/ToastController.tsx | 5 ++++- src/toast/ToastProvider.tsx | 13 +++++++------ src/toast/ToastState.ts | 21 +++++++++++++++++++++ src/toast/index.ts | 2 +- src/toast/stories/AnimatedToast.stories.tsx | 4 ++-- src/toast/stories/BasicToast.stories.tsx | 2 +- src/utils/index.ts | 5 ++--- 8 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/timepicker/__utils.ts b/src/timepicker/__utils.ts index 75682e83d..d78dbc8f6 100644 --- a/src/timepicker/__utils.ts +++ b/src/timepicker/__utils.ts @@ -4,7 +4,7 @@ import { isString } from "@chakra-ui/utils"; import { ColumnType } from "./TimePickerColumnState"; // As per https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#times -export function parseTime(timeValue: string | undefined) { +export function parseTime(timeValue?: string) { if (timeValue == null || !isString(timeValue)) return; const timeRegex = timeValue.match(/(\d\d)(:(\d\d)){1,2}/i); diff --git a/src/toast/ToastController.tsx b/src/toast/ToastController.tsx index 1ff7d93cb..7c1abdde0 100644 --- a/src/toast/ToastController.tsx +++ b/src/toast/ToastController.tsx @@ -2,6 +2,9 @@ import React from "react"; import { useTimeout } from "@chakra-ui/hooks"; interface ToastControllerProps { + /** + * unique id of the toast + */ id: string; /** * Duration after the toast will be removed automatically if autoDismiss is true @@ -20,7 +23,7 @@ interface ToastControllerProps { */ dragThreshold?: number; /** - * Callback will fire after the toast has been removed + * Callback to fire when toast is flagged as removed */ onRequestRemove: (id: string) => void; } diff --git a/src/toast/ToastProvider.tsx b/src/toast/ToastProvider.tsx index 636863bb5..a75ce93a6 100644 --- a/src/toast/ToastProvider.tsx +++ b/src/toast/ToastProvider.tsx @@ -3,6 +3,7 @@ import ReactDOM from "react-dom"; import { canUseDOM } from "reakit-utils"; import { createContext } from "@chakra-ui/utils"; +import { isFunction } from "../utils"; import { ToastStateReturn } from "./ToastState"; import { ToastController } from "./ToastController"; import { useToastState, IToast } from "./ToastState"; @@ -64,7 +65,7 @@ type IToastProvider = { */ animationTimeout?: number; /** - * Callback function to enhance the behaviour of ToastControllers + * Wrapper function to enhance the behaviour of ToastController */ toastWrapper?: TToastWrapper; /** @@ -98,7 +99,7 @@ export const ToastProvider: React.FC = ({ }} > {toastList.map( - ({ id, type, content, timeout, autoDismiss, isVisible }) => { + ({ id, type = "", content, timeout, autoDismiss, isVisible }) => { return ( = ({ duration={timeout ?? providerTimeout} autoDismiss={autoDismiss ?? providerAutoDismiss} > - {typeof content === "function" + {isFunction(content) ? content({ id, isVisible, remove: state.hide }) - : toastTypes[type || ""]?.({ - content, + : toastTypes[type]?.({ id, - remove: state.hide, + content, isVisible, + remove: state.hide, }) || content} diff --git a/src/toast/ToastState.ts b/src/toast/ToastState.ts index 2a7eae7c1..020194e16 100644 --- a/src/toast/ToastState.ts +++ b/src/toast/ToastState.ts @@ -6,12 +6,33 @@ import { Placements } from "./ToastProvider"; type StringOrElement = string | React.ReactNode; export interface IToast { + /** + * uniue id of toast + */ id: string; + /** + * type of toast + */ type?: string; + /** + * content inside the toast + */ content: StringOrElement; + /** + * Timeout after the toast will be removed automatically if autoDismiss is true + */ timeout?: number; + /** + * sets the placement of the toast + */ placement?: Placements; + /** + * If True the toast will automatically dismiss after the specified duration + */ autoDismiss?: boolean; + /** + * Sets toast initial visibility + */ isVisible?: boolean; } diff --git a/src/toast/index.ts b/src/toast/index.ts index 27177dd34..a5b671442 100644 --- a/src/toast/index.ts +++ b/src/toast/index.ts @@ -1,3 +1,3 @@ -export * from "./ToastController"; export * from "./ToastState"; export * from "./ToastProvider"; +export * from "./ToastController"; diff --git a/src/toast/stories/AnimatedToast.stories.tsx b/src/toast/stories/AnimatedToast.stories.tsx index f58ba9792..7b041b184 100644 --- a/src/toast/stories/AnimatedToast.stories.tsx +++ b/src/toast/stories/AnimatedToast.stories.tsx @@ -1,9 +1,9 @@ +import "./style.css"; import React from "react"; import { Meta } from "@storybook/react"; -import { useTransition, animated } from "react-spring"; import { CSSTransition } from "react-transition-group"; +import { useTransition, animated } from "react-spring"; -import "./style.css"; import Demo, { getTransform } from "./Demo"; import { ToastProvider, TToastWrapper } from "../index"; diff --git a/src/toast/stories/BasicToast.stories.tsx b/src/toast/stories/BasicToast.stories.tsx index 25aa4690b..17ca64246 100644 --- a/src/toast/stories/BasicToast.stories.tsx +++ b/src/toast/stories/BasicToast.stories.tsx @@ -1,7 +1,7 @@ +import "./style.css"; import React from "react"; import { Meta } from "@storybook/react"; -import "./style.css"; import Demo from "./Demo"; import { ToastProvider } from "../index"; diff --git a/src/utils/index.ts b/src/utils/index.ts index a03e5c357..49d7cc17a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -45,10 +45,9 @@ export function isTouch() { ); } -export const dataAttr = (condition: boolean | undefined) => +export const dataAttr = (condition?: boolean) => (condition ? "" : undefined) as Booleanish; -export const ariaAttr = (condition: boolean | undefined) => - condition ? true : undefined; +export const ariaAttr = (condition?: boolean) => (condition ? true : undefined); export * from "./date"; From 8cf504a5609bc9e9ca26b126a25489b9d2eae616 Mon Sep 17 00:00:00 2001 From: Anurag Date: Tue, 20 Oct 2020 16:09:04 +0530 Subject: [PATCH 4/5] chore: use strict parameters --- src/timepicker/__utils.ts | 2 +- src/utils/index.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/timepicker/__utils.ts b/src/timepicker/__utils.ts index d78dbc8f6..75682e83d 100644 --- a/src/timepicker/__utils.ts +++ b/src/timepicker/__utils.ts @@ -4,7 +4,7 @@ import { isString } from "@chakra-ui/utils"; import { ColumnType } from "./TimePickerColumnState"; // As per https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#times -export function parseTime(timeValue?: string) { +export function parseTime(timeValue: string | undefined) { if (timeValue == null || !isString(timeValue)) return; const timeRegex = timeValue.match(/(\d\d)(:(\d\d)){1,2}/i); diff --git a/src/utils/index.ts b/src/utils/index.ts index 49d7cc17a..a03e5c357 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -45,9 +45,10 @@ export function isTouch() { ); } -export const dataAttr = (condition?: boolean) => +export const dataAttr = (condition: boolean | undefined) => (condition ? "" : undefined) as Booleanish; -export const ariaAttr = (condition?: boolean) => (condition ? true : undefined); +export const ariaAttr = (condition: boolean | undefined) => + condition ? true : undefined; export * from "./date"; From d9133560098fe07eeeacf97a1027d458150a359e Mon Sep 17 00:00:00 2001 From: Anurag Date: Wed, 21 Oct 2020 11:57:44 +0530 Subject: [PATCH 5/5] chore: extract createContext --- src/toast/ToastProvider.tsx | 3 +-- src/utils/index.ts | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/toast/ToastProvider.tsx b/src/toast/ToastProvider.tsx index a75ce93a6..b0e58e40d 100644 --- a/src/toast/ToastProvider.tsx +++ b/src/toast/ToastProvider.tsx @@ -1,12 +1,11 @@ import React from "react"; import ReactDOM from "react-dom"; import { canUseDOM } from "reakit-utils"; -import { createContext } from "@chakra-ui/utils"; -import { isFunction } from "../utils"; import { ToastStateReturn } from "./ToastState"; import { ToastController } from "./ToastController"; import { useToastState, IToast } from "./ToastState"; +import { isFunction, createContext } from "../utils"; const DEFAULT_TIMEOUT = 5000; const PLACEMENTS = { diff --git a/src/utils/index.ts b/src/utils/index.ts index a03e5c357..b748d63ab 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,4 @@ +import React from "react"; import { Booleanish } from "./types"; import { warn } from "@chakra-ui/utils"; @@ -51,4 +52,53 @@ export const dataAttr = (condition: boolean | undefined) => export const ariaAttr = (condition: boolean | undefined) => condition ? true : undefined; +export interface CreateContextOptions { + /** + * If `true`, React will throw if context is `null` or `undefined` + * In some cases, you might want to support nested context, so you can set it to `false` + */ + strict?: boolean; + /** + * Error message to throw if the context is `undefined` + */ + errorMessage?: string; + /** + * The display name of the context + */ + name?: string; +} + +type CreateContextReturn = [React.Provider, () => T, React.Context]; + +/** + * Creates a named context, provider, and hook. + * + * @param options create context options + */ +export function createContext(options: CreateContextOptions = {}) { + const { + strict = true, + errorMessage = "useContext: `context` is undefined. Seems you forgot to wrap component within the Provider", + name, + } = options; + + const Context = React.createContext(undefined); + + Context.displayName = name; + + function useContext() { + const context = React.useContext(Context); + + if (!context && strict) { + throw new Error(errorMessage); + } + + return context; + } + + return [Context.Provider, useContext, Context] as CreateContextReturn< + ContextType + >; +} + export * from "./date";