@servicetitan/anvil2-ext-common 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @servicetitan/anvil2-ext-common
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`d97ef4d`](https://github.com/servicetitan/hammer/commit/d97ef4d33c6240831a48b38e70305a6b5eb33d6d) Thanks [@rgdelato](https://github.com/rgdelato)! - [ext-common] Fix `package.json` `files` property
8
+
3
9
  ## 0.1.0
4
10
 
5
11
  ### Minor Changes
@@ -0,0 +1 @@
1
+ export * from './useConfirmDialog';
@@ -0,0 +1,74 @@
1
+ import { ComponentProps, ComponentPropsWithRef, FC, ReactNode } from 'react';
2
+ import { Button, Dialog } from '@servicetitan/anvil2';
3
+ import { Prettify } from '../../types';
4
+ export interface PrimaryButtonInternalProps {
5
+ appearance?: ComponentProps<typeof Button>["appearance"];
6
+ onClick?: () => void;
7
+ children: ReactNode;
8
+ }
9
+ export interface ConfirmDialogInternalProps {
10
+ /**
11
+ * Callback function when the confirm button is clicked
12
+ */
13
+ onConfirm: () => void;
14
+ /**
15
+ * Content to be displayed in the dialog
16
+ * @defaultValue 'Are you sure?'
17
+ */
18
+ children?: ReactNode;
19
+ /**
20
+ * Appearance of the primary button
21
+ * @defaultValue 'primary'
22
+ */
23
+ appearance?: PrimaryButtonInternalProps["appearance"];
24
+ /**
25
+ * Header content of the dialog
26
+ * @defaultValue 'Confirm'
27
+ */
28
+ header?: ReactNode;
29
+ /**
30
+ * Content of the primary button
31
+ * @defaultValue 'OK'
32
+ */
33
+ primaryButton?: ReactNode;
34
+ /**
35
+ * Content of the cancel button
36
+ * @defaultValue 'Cancel'
37
+ */
38
+ cancelButton?: ReactNode;
39
+ /**
40
+ * Props to be passed to the internal <Dialog.Header>
41
+ */
42
+ headerProps?: Omit<ComponentPropsWithRef<typeof Dialog.Header>, "children">;
43
+ /**
44
+ * Props to be passed to the internal <Dialog.Content>
45
+ */
46
+ contentProps?: Omit<ComponentPropsWithRef<typeof Dialog.Content>, "children">;
47
+ /**
48
+ * Props to be passed to the internal <Dialog.Footer>
49
+ */
50
+ footerProps?: Omit<ComponentPropsWithRef<typeof Dialog.Footer>, "children">;
51
+ /**
52
+ * Props to be passed to the internal <Dialog.CancelButton>
53
+ */
54
+ cancelButtonProps?: Omit<ComponentPropsWithRef<typeof Dialog.CancelButton>, "children">;
55
+ /**
56
+ * Props to be passed to the internal primary <Button>
57
+ */
58
+ primaryButtonProps?: Omit<ComponentPropsWithRef<typeof Button>, keyof PrimaryButtonInternalProps>;
59
+ }
60
+ export type ConfirmDialogProps = Prettify<ConfirmDialogInternalProps & Omit<ComponentPropsWithRef<typeof Dialog>, keyof ConfirmDialogInternalProps>>;
61
+ /**
62
+ * ConfirmDialog component for displaying a simple confirmation dialog.
63
+ *
64
+ * @remarks
65
+ * - Any property of Anvil's <Dialog> can be passed directly to <ConfirmDialog>
66
+ *
67
+ * - header, primaryButton, and cancelButton are typed as ReactNode,
68
+ * so arbitrary React markup can be passed in addition to strings.
69
+ *
70
+ * - headerProps, contentProps, footerProps, cancelButtonProps, and primaryButtonProps
71
+ * can be used to customize the internal components. They will be forwarded directly
72
+ * to <Dialog.Header>, <Dialog.Content>, <Dialog.Footer>, etc.
73
+ */
74
+ export declare const ConfirmDialog: FC<ConfirmDialogProps>;
@@ -0,0 +1 @@
1
+ export { useConfirmDialog, useConfirmDialog as default, } from './useConfirmDialog';
@@ -1,11 +1,6 @@
1
- import { useState } from "react";
2
- import { ConfirmDialog, ConfirmDialogProps } from "./ConfirmDialog";
3
- import { Optional, Prettify } from "../../types";
4
-
5
- export type UseConfirmDialogProps = Prettify<
6
- Omit<Optional<ConfirmDialogProps, "open">, "onClose" | "onConfirm">
7
- >;
8
-
1
+ import { ConfirmDialogProps } from './ConfirmDialog';
2
+ import { Optional, Prettify } from '../../types';
3
+ export type UseConfirmDialogProps = Prettify<Omit<Optional<ConfirmDialogProps, "open">, "onClose" | "onConfirm">>;
9
4
  /**
10
5
  * useConfirmDialog hook for displaying a confirmation dialog.
11
6
  * - simpler markup than building a whole <Dialog>
@@ -56,34 +51,4 @@ export type UseConfirmDialogProps = Prettify<
56
51
  * - The following properties are passed to the internal components:
57
52
  * `headerProps`, `contentProps`, `footerProps`, `cancelButtonProps`, `primaryButtonProps`
58
53
  */
59
- export const useConfirmDialog = (
60
- onConfirm: () => void,
61
- onCancel?: () => void,
62
- ) => {
63
- const [isOpen, setIsOpen] = useState(false);
64
-
65
- const onConfirmInner = () => {
66
- onConfirm();
67
- setIsOpen(false);
68
- };
69
-
70
- const closeDialogInner = () => {
71
- onCancel?.();
72
- setIsOpen(false);
73
- };
74
-
75
- const UseConfirmDialogInner = (props: UseConfirmDialogProps) => (
76
- <ConfirmDialog
77
- open={isOpen}
78
- {...props} // allow `open` passed to <ConfirmDialog> to override internal `isOpen`
79
- onConfirm={onConfirmInner}
80
- onClose={closeDialogInner}
81
- />
82
- );
83
-
84
- return [
85
- UseConfirmDialogInner,
86
- () => setIsOpen(true),
87
- () => setIsOpen(false),
88
- ] as const;
89
- };
54
+ export declare const useConfirmDialog: (onConfirm: () => void, onCancel?: () => void) => readonly [(props: UseConfirmDialogProps) => import("react/jsx-runtime").JSX.Element, () => void, () => void];
@@ -0,0 +1,2 @@
1
+ export * from './hooks';
2
+ export * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { u as useConfirmDialog } from './useConfirmDialog-DaQ5Golt.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
2
+ export type Prettify<T> = {
3
+ [K in keyof T]: T[K];
4
+ } & {};
@@ -0,0 +1,61 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import { useState } from 'react';
3
+ import { Dialog, Button } from '@servicetitan/anvil2';
4
+
5
+ const ConfirmDialog = ({
6
+ appearance = "primary",
7
+ header = "Confirm",
8
+ children = "Are you sure?",
9
+ primaryButton = "OK",
10
+ cancelButton = "Cancel",
11
+ onConfirm,
12
+ cancelButtonProps,
13
+ primaryButtonProps,
14
+ headerProps,
15
+ contentProps,
16
+ footerProps,
17
+ ...dialogProps
18
+ }) => {
19
+ const primaryButtonInternalProps = {
20
+ appearance,
21
+ onClick: onConfirm,
22
+ children: primaryButton
23
+ };
24
+ return /* @__PURE__ */ jsxs(Dialog, { ...dialogProps, children: [
25
+ /* @__PURE__ */ jsx(Dialog.Header, { ...headerProps, children: header }),
26
+ /* @__PURE__ */ jsx(Dialog.Content, { ...contentProps, children }),
27
+ /* @__PURE__ */ jsxs(Dialog.Footer, { ...footerProps, children: [
28
+ /* @__PURE__ */ jsx(Dialog.CancelButton, { ...cancelButtonProps, children: cancelButton }),
29
+ /* @__PURE__ */ jsx(Button, { ...primaryButtonInternalProps, ...primaryButtonProps })
30
+ ] })
31
+ ] });
32
+ };
33
+
34
+ const useConfirmDialog = (onConfirm, onCancel) => {
35
+ const [isOpen, setIsOpen] = useState(false);
36
+ const onConfirmInner = () => {
37
+ onConfirm();
38
+ setIsOpen(false);
39
+ };
40
+ const closeDialogInner = () => {
41
+ onCancel?.();
42
+ setIsOpen(false);
43
+ };
44
+ const UseConfirmDialogInner = (props) => /* @__PURE__ */ jsx(
45
+ ConfirmDialog,
46
+ {
47
+ open: isOpen,
48
+ ...props,
49
+ onConfirm: onConfirmInner,
50
+ onClose: closeDialogInner
51
+ }
52
+ );
53
+ return [
54
+ UseConfirmDialogInner,
55
+ () => setIsOpen(true),
56
+ () => setIsOpen(false)
57
+ ];
58
+ };
59
+
60
+ export { useConfirmDialog as u };
61
+ //# sourceMappingURL=useConfirmDialog-DaQ5Golt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConfirmDialog-DaQ5Golt.js","sources":["../src/hooks/useConfirmDialog/ConfirmDialog.tsx","../src/hooks/useConfirmDialog/useConfirmDialog.tsx"],"sourcesContent":["import { ComponentProps, ComponentPropsWithRef, FC, ReactNode } from \"react\";\nimport { Button, Dialog } from \"@servicetitan/anvil2\";\nimport { Prettify } from \"../../types\";\n\nexport interface PrimaryButtonInternalProps {\n appearance?: ComponentProps<typeof Button>[\"appearance\"];\n onClick?: () => void;\n children: ReactNode;\n}\n\nexport interface ConfirmDialogInternalProps {\n /**\n * Callback function when the confirm button is clicked\n */\n onConfirm: () => void;\n\n /**\n * Content to be displayed in the dialog\n * @defaultValue 'Are you sure?'\n */\n children?: ReactNode;\n\n /**\n * Appearance of the primary button\n * @defaultValue 'primary'\n */\n appearance?: PrimaryButtonInternalProps[\"appearance\"];\n\n /**\n * Header content of the dialog\n * @defaultValue 'Confirm'\n */\n header?: ReactNode;\n\n /**\n * Content of the primary button\n * @defaultValue 'OK'\n */\n primaryButton?: ReactNode;\n\n /**\n * Content of the cancel button\n * @defaultValue 'Cancel'\n */\n cancelButton?: ReactNode;\n\n /**\n * Props to be passed to the internal <Dialog.Header>\n */\n headerProps?: Omit<ComponentPropsWithRef<typeof Dialog.Header>, \"children\">;\n\n /**\n * Props to be passed to the internal <Dialog.Content>\n */\n contentProps?: Omit<ComponentPropsWithRef<typeof Dialog.Content>, \"children\">;\n\n /**\n * Props to be passed to the internal <Dialog.Footer>\n */\n footerProps?: Omit<ComponentPropsWithRef<typeof Dialog.Footer>, \"children\">;\n\n /**\n * Props to be passed to the internal <Dialog.CancelButton>\n */\n cancelButtonProps?: Omit<\n ComponentPropsWithRef<typeof Dialog.CancelButton>,\n \"children\"\n >;\n\n /**\n * Props to be passed to the internal primary <Button>\n */\n primaryButtonProps?: Omit<\n ComponentPropsWithRef<typeof Button>,\n keyof PrimaryButtonInternalProps\n >;\n}\n\nexport type ConfirmDialogProps = Prettify<\n ConfirmDialogInternalProps &\n Omit<ComponentPropsWithRef<typeof Dialog>, keyof ConfirmDialogInternalProps>\n>;\n\n/**\n * ConfirmDialog component for displaying a simple confirmation dialog.\n *\n * @remarks\n * - Any property of Anvil's <Dialog> can be passed directly to <ConfirmDialog>\n *\n * - header, primaryButton, and cancelButton are typed as ReactNode,\n * so arbitrary React markup can be passed in addition to strings.\n *\n * - headerProps, contentProps, footerProps, cancelButtonProps, and primaryButtonProps\n * can be used to customize the internal components. They will be forwarded directly\n * to <Dialog.Header>, <Dialog.Content>, <Dialog.Footer>, etc.\n */\nexport const ConfirmDialog: FC<ConfirmDialogProps> = ({\n appearance = \"primary\",\n header = \"Confirm\",\n children = \"Are you sure?\",\n primaryButton = \"OK\",\n cancelButton = \"Cancel\",\n onConfirm,\n cancelButtonProps,\n primaryButtonProps,\n headerProps,\n contentProps,\n footerProps,\n ...dialogProps\n}: ConfirmDialogProps) => {\n const primaryButtonInternalProps: PrimaryButtonInternalProps = {\n appearance,\n onClick: onConfirm,\n children: primaryButton,\n };\n\n return (\n <Dialog {...dialogProps}>\n <Dialog.Header {...headerProps}>{header}</Dialog.Header>\n <Dialog.Content {...contentProps}>{children}</Dialog.Content>\n <Dialog.Footer {...footerProps}>\n <Dialog.CancelButton {...cancelButtonProps}>\n {cancelButton}\n </Dialog.CancelButton>\n\n {/* allow primaryButtonProps object to override the explicit top-level props - primaryButton, appearance, onClick */}\n <Button {...primaryButtonInternalProps} {...primaryButtonProps} />\n </Dialog.Footer>\n </Dialog>\n );\n};\n","import { useState } from \"react\";\nimport { ConfirmDialog, ConfirmDialogProps } from \"./ConfirmDialog\";\nimport { Optional, Prettify } from \"../../types\";\n\nexport type UseConfirmDialogProps = Prettify<\n Omit<Optional<ConfirmDialogProps, \"open\">, \"onClose\" | \"onConfirm\">\n>;\n\n/**\n * useConfirmDialog hook for displaying a confirmation dialog.\n * - simpler markup than building a whole <Dialog>\n * - no need to explicitly manage its `open` state\n *\n * @param onConfirm (required) - Function to call when the confirmation action is taken.\n * @param onClose (optional) - Function to call when the dialog is closed.\n *\n * @example\n * ```ts\n * const [ConfirmDelete, onDelete] = useConfirmDialog(store.onDelete);\n *\n * // Then in markup below:\n * <ConfirmDelete header=\"Confirm Deletion\" primaryButton=\"Delete\" appearance=\"danger\">\n * Are you sure you want to delete this item?\n * </ConfirmDelete>\n *\n * // If no children are supplied, the default message 'Are you sure?' will be used.\n * <ConfirmDelete />\n * ```\n *\n * @example\n * If you want to manually control the `open` state of the ConfirmDialog:\n * ```ts\n * const [ConfirmDelete] = useConfirmDialog(store.onDelete);\n *\n * // Then in markup below:\n * <ConfirmDelete open={showConfirmDelete} header=\"Confirm Deletion\" primaryButton=\"Delete\" appearance=\"danger\">\n * Are you sure you want to delete this item?\n * </ConfirmDelete>\n * ```\n *\n * @remarks\n * - The returned `ConfirmDialog` component will handle its own open/close state,\n * or you can override it by passing the `open` prop to it.\n *\n * - The `onConfirm` function will be called when the user confirms the action.\n *\n * - If provided, the `onCancel` function will be called when the dialog is cancelled\n * (either by clicking the cancel button, the close button, or outside the dialog).\n *\n * - If you need to customize the dialog further:\n * - Any property of Anvil2's <Dialog> can be passed to <ConfirmDialog> except `open` and `onClose`\n *\n * - The `header`, `primaryButton`, and `cancelButton` properties are typed as ReactNode,\n * so arbitrary React markup can be passed in addition to strings.\n *\n * - The following properties are passed to the internal components:\n * `headerProps`, `contentProps`, `footerProps`, `cancelButtonProps`, `primaryButtonProps`\n */\nexport const useConfirmDialog = (\n onConfirm: () => void,\n onCancel?: () => void,\n) => {\n const [isOpen, setIsOpen] = useState(false);\n\n const onConfirmInner = () => {\n onConfirm();\n setIsOpen(false);\n };\n\n const closeDialogInner = () => {\n onCancel?.();\n setIsOpen(false);\n };\n\n const UseConfirmDialogInner = (props: UseConfirmDialogProps) => (\n <ConfirmDialog\n open={isOpen}\n {...props} // allow `open` passed to <ConfirmDialog> to override internal `isOpen`\n onConfirm={onConfirmInner}\n onClose={closeDialogInner}\n />\n );\n\n return [\n UseConfirmDialogInner,\n () => setIsOpen(true),\n () => setIsOpen(false),\n ] as const;\n};\n"],"names":[],"mappings":";;;;AAgGO,MAAM,gBAAwC,CAAC;AAAA,EACpD,UAAA,GAAa,SAAA;AAAA,EACb,MAAA,GAAS,SAAA;AAAA,EACT,QAAA,GAAW,eAAA;AAAA,EACX,aAAA,GAAgB,IAAA;AAAA,EAChB,YAAA,GAAe,QAAA;AAAA,EACf,SAAA;AAAA,EACA,iBAAA;AAAA,EACA,kBAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,GAAG;AACL,CAAA,KAA0B;AACxB,EAAA,MAAM,0BAAA,GAAyD;AAAA,IAC7D,UAAA;AAAA,IACA,OAAA,EAAS,SAAA;AAAA,IACT,QAAA,EAAU;AAAA,GACZ;AAEA,EAAA,uBACE,IAAA,CAAC,MAAA,EAAA,EAAQ,GAAG,WAAA,EACV,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,CAAO,MAAA,EAAP,EAAe,GAAG,aAAc,QAAA,EAAA,MAAA,EAAO,CAAA;AAAA,wBACvC,MAAA,CAAO,OAAA,EAAP,EAAgB,GAAG,cAAe,QAAA,EAAS,CAAA;AAAA,oBAC5C,IAAA,CAAC,MAAA,CAAO,MAAA,EAAP,EAAe,GAAG,WAAA,EACjB,QAAA,EAAA;AAAA,sBAAA,GAAA,CAAC,MAAA,CAAO,YAAA,EAAP,EAAqB,GAAG,mBACtB,QAAA,EAAA,YAAA,EACH,CAAA;AAAA,sBAGA,GAAA,CAAC,MAAA,EAAA,EAAQ,GAAG,0BAAA,EAA6B,GAAG,kBAAA,EAAoB;AAAA,KAAA,EAClE;AAAA,GAAA,EACF,CAAA;AAEJ,CAAA;;ACxEO,MAAM,gBAAA,GAAmB,CAC9B,SAAA,EACA,QAAA,KACG;AACH,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,KAAK,CAAA;AAE1C,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,SAAA,EAAU;AACV,IAAA,SAAA,CAAU,KAAK,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,mBAAmB,MAAM;AAC7B,IAAA,QAAA,IAAW;AACX,IAAA,SAAA,CAAU,KAAK,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,qBAAA,GAAwB,CAAC,KAAA,qBAC7B,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,IAAA,EAAM,MAAA;AAAA,MACL,GAAG,KAAA;AAAA,MACJ,SAAA,EAAW,cAAA;AAAA,MACX,OAAA,EAAS;AAAA;AAAA,GACX;AAGF,EAAA,OAAO;AAAA,IACL,qBAAA;AAAA,IACA,MAAM,UAAU,IAAI,CAAA;AAAA,IACpB,MAAM,UAAU,KAAK;AAAA,GACvB;AACF;;;;"}
@@ -0,0 +1,6 @@
1
+ export * from './hooks/useConfirmDialog/index'
2
+ export {}
3
+ import _default from './hooks/useConfirmDialog/index'
4
+ export default _default
5
+ export * from './hooks/useConfirmDialog/index'
6
+ export {}
@@ -0,0 +1,2 @@
1
+ export { u as default, u as useConfirmDialog } from './useConfirmDialog-DaQ5Golt.js';
2
+ //# sourceMappingURL=useConfirmDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useConfirmDialog.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,9 +1,22 @@
1
1
  {
2
2
  "name": "@servicetitan/anvil2-ext-common",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
7
+ "module": ".",
8
+ "sideEffects": [
9
+ "*.css",
10
+ "*.scss"
11
+ ],
12
+ "files": [
13
+ "dist",
14
+ "CHANGELOG.md"
15
+ ],
16
+ "exports": {
17
+ ".": "./dist/index.js",
18
+ "./package.json": "./package.json"
19
+ },
7
20
  "keywords": [],
8
21
  "author": "",
9
22
  "license": "ISC",
@@ -1,35 +0,0 @@
1
- import type { StorybookConfig } from "@storybook/react-vite";
2
- import { join, dirname } from "path";
3
-
4
- /**
5
- * This function is used to resolve the absolute path of a package.
6
- * It is needed in projects that use Yarn PnP or are set up within a monorepo.
7
- */
8
- function getAbsolutePath(value: string): string {
9
- return dirname(require.resolve(join(value, "package.json")));
10
- }
11
- const config: StorybookConfig = {
12
- stories: [
13
- "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
14
- "../src/**/*.stories.mdx",
15
- ],
16
- features: {
17
- backgrounds: true,
18
- viewport: true,
19
- },
20
- addons: [
21
- getAbsolutePath("@storybook/addon-a11y"),
22
- getAbsolutePath("@storybook/addon-links"),
23
- getAbsolutePath("@chromatic-com/storybook"),
24
- getAbsolutePath("@storybook/addon-vitest"),
25
- ],
26
- framework: {
27
- name: getAbsolutePath("@storybook/react-vite"),
28
- options: {},
29
- },
30
- refs: {
31
- "@servicetitan/anvil2": { disable: true },
32
- "@servicetitan/design-system": { disable: true },
33
- },
34
- };
35
- export default config;
@@ -1 +0,0 @@
1
- <link rel="icon" type="image/x-icon" href="/favicon.ico" />
File without changes
@@ -1,203 +0,0 @@
1
- import type { Preview } from "@storybook/react-vite";
2
- import * as anvil2 from "@servicetitan/anvil2";
3
- import { core } from "@servicetitan/anvil2/token";
4
-
5
- const { AnvilProvider, Flex } = anvil2;
6
-
7
- const layoutProps = [
8
- "flex",
9
- "flexGrow",
10
- "flexShrink",
11
- "flexBasis",
12
- "alignSelf",
13
- "justifySelf",
14
- "order",
15
- "gridArea",
16
- "gridColumn",
17
- "gridRow",
18
- "gridTemplate",
19
- "gridColumnStart",
20
- "gridColumnEnd",
21
- "gridRowStart",
22
- "gridRowEnd",
23
- "sm",
24
- "md",
25
- "lg",
26
- "xl",
27
- "xxl",
28
- ];
29
-
30
- // we /could/ detect the actual scrollbar width here, but this is good enough
31
- function addScrollbarWidth(s?: string) {
32
- return s ? parseInt(s) + 16 + "px" : s;
33
- }
34
-
35
- const viewports = {
36
- breakpointSmall: {
37
- name: "Breakpoint Small",
38
- styles: {
39
- width: addScrollbarWidth(core.primitive?.BreakpointSm?.value),
40
- height: "100%",
41
- },
42
- },
43
- breakpointMedium: {
44
- name: "Breakpoint Medium",
45
- styles: {
46
- width: addScrollbarWidth(core.primitive?.BreakpointMd?.value),
47
- height: "100%",
48
- },
49
- },
50
- breakpointLarge: {
51
- name: "Breakpoint Large",
52
- styles: {
53
- width: addScrollbarWidth(core.primitive?.BreakpointLg?.value),
54
- height: "100%",
55
- },
56
- },
57
- breakpointXLarge: {
58
- name: "Breakpoint X-Large",
59
- styles: {
60
- width: addScrollbarWidth(core.primitive?.BreakpointXl?.value),
61
- height: "100%",
62
- },
63
- },
64
- breakpointXXLarge: {
65
- name: "Breakpoint XX-Large",
66
- styles: {
67
- width: addScrollbarWidth(core.primitive?.BreakpointXxl?.value),
68
- height: "100%",
69
- },
70
- },
71
- };
72
-
73
- const preview: Preview = {
74
- globalTypes: {
75
- shadow: {
76
- description: "Whether or not to use a shadow DOM",
77
- toolbar: {
78
- title: "Shadow",
79
- icon: "mirror",
80
- items: [
81
- {
82
- value: "fragment",
83
- icon: "switchalt",
84
- title: "Fragment",
85
- },
86
- {
87
- value: "shadow",
88
- icon: "contrast",
89
- title: "Shadow",
90
- },
91
- ],
92
- dynamicTitle: true,
93
- },
94
- },
95
- dir: {
96
- description: "Indicates the directionality of the element's text",
97
- toolbar: {
98
- title: "dir",
99
- items: [
100
- {
101
- value: "ltr",
102
- title: "LTR",
103
- },
104
- {
105
- value: "rtl",
106
- title: "RTL",
107
- },
108
- ],
109
- dynamicTitle: true,
110
- },
111
- },
112
- },
113
- parameters: {
114
- actions: { argTypesRegex: "^on.*" },
115
-
116
- backgrounds: {
117
- options: {
118
- dark: { name: "Dark", value: "#141414" },
119
- light: { name: "Light", value: "#FFFFFF" },
120
- system: { name: "System", value: undefined },
121
- },
122
- },
123
-
124
- chromatic: {
125
- prefersReducedMotion: "reduce",
126
- pauseAnimationAtEnd: true,
127
- },
128
-
129
- docs: {
130
- argTypes: {
131
- sort: "requiredFirst",
132
- },
133
- codePanel: true,
134
- },
135
-
136
- controls: {
137
- sort: "requiredFirst",
138
- matchers: {
139
- color: /(background|color)$/i,
140
- date: /Date$/i,
141
- },
142
- expanded: true,
143
- exclude: layoutProps,
144
- },
145
-
146
- layout: "fullscreen",
147
- viewport: { options: viewports },
148
-
149
- a11y: {
150
- // 'todo' - show a11y violations in the test UI only
151
- // 'error' - fail CI on a11y violations
152
- // 'off' - skip a11y checks entirely
153
- test: "todo",
154
- },
155
- },
156
- initialGlobals: {
157
- shadow: "shadow",
158
- dir: "ltr",
159
- backgrounds: { value: "system" },
160
- },
161
- decorators: [
162
- (Story, context) => {
163
- const { minHeight, noPadding } = context.parameters;
164
- const mode =
165
- context.globals.backgrounds.value === "system"
166
- ? undefined
167
- : context.globals.backgrounds.value;
168
-
169
- if (context.title.startsWith("Anvil2")) {
170
- return (
171
- <AnvilProvider themeData={{ mode }} dir={context.globals.dir}>
172
- <Flex
173
- className="bootstrap"
174
- alignItems="flex-start"
175
- style={{
176
- backgroundColor: `var(--background-color)`,
177
- padding: noPadding ? undefined : "1rem",
178
- minHeight,
179
- }}
180
- >
181
- <Story />
182
- </Flex>
183
- </AnvilProvider>
184
- );
185
- }
186
- return (
187
- <Flex
188
- dir={context.globals.dir}
189
- alignItems="flex-start"
190
- style={{
191
- backgroundColor: `var(--background-color)`,
192
- padding: "1rem",
193
- minHeight,
194
- }}
195
- >
196
- <Story />
197
- </Flex>
198
- );
199
- },
200
- ],
201
- };
202
-
203
- export default preview;
@@ -1,7 +0,0 @@
1
- import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
2
- import { setProjectAnnotations } from "@storybook/react-vite";
3
- import * as projectAnnotations from "./preview";
4
-
5
- // This is an important step to apply the right configuration when testing your stories.
6
- // More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
7
- setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
package/eslint.config.js DELETED
@@ -1,5 +0,0 @@
1
- import baseConfig from "../../eslint.base.config.mjs";
2
- import reactConfig from "../../eslint.react.config.mjs";
3
- import eslintConfigPrettier from "eslint-config-prettier/flat";
4
-
5
- export default [...baseConfig, ...reactConfig, eslintConfigPrettier];
@@ -1 +0,0 @@
1
- export * from "./useConfirmDialog";
@@ -1,131 +0,0 @@
1
- import { ComponentProps, ComponentPropsWithRef, FC, ReactNode } from "react";
2
- import { Button, Dialog } from "@servicetitan/anvil2";
3
- import { Prettify } from "../../types";
4
-
5
- export interface PrimaryButtonInternalProps {
6
- appearance?: ComponentProps<typeof Button>["appearance"];
7
- onClick?: () => void;
8
- children: ReactNode;
9
- }
10
-
11
- export interface ConfirmDialogInternalProps {
12
- /**
13
- * Callback function when the confirm button is clicked
14
- */
15
- onConfirm: () => void;
16
-
17
- /**
18
- * Content to be displayed in the dialog
19
- * @defaultValue 'Are you sure?'
20
- */
21
- children?: ReactNode;
22
-
23
- /**
24
- * Appearance of the primary button
25
- * @defaultValue 'primary'
26
- */
27
- appearance?: PrimaryButtonInternalProps["appearance"];
28
-
29
- /**
30
- * Header content of the dialog
31
- * @defaultValue 'Confirm'
32
- */
33
- header?: ReactNode;
34
-
35
- /**
36
- * Content of the primary button
37
- * @defaultValue 'OK'
38
- */
39
- primaryButton?: ReactNode;
40
-
41
- /**
42
- * Content of the cancel button
43
- * @defaultValue 'Cancel'
44
- */
45
- cancelButton?: ReactNode;
46
-
47
- /**
48
- * Props to be passed to the internal <Dialog.Header>
49
- */
50
- headerProps?: Omit<ComponentPropsWithRef<typeof Dialog.Header>, "children">;
51
-
52
- /**
53
- * Props to be passed to the internal <Dialog.Content>
54
- */
55
- contentProps?: Omit<ComponentPropsWithRef<typeof Dialog.Content>, "children">;
56
-
57
- /**
58
- * Props to be passed to the internal <Dialog.Footer>
59
- */
60
- footerProps?: Omit<ComponentPropsWithRef<typeof Dialog.Footer>, "children">;
61
-
62
- /**
63
- * Props to be passed to the internal <Dialog.CancelButton>
64
- */
65
- cancelButtonProps?: Omit<
66
- ComponentPropsWithRef<typeof Dialog.CancelButton>,
67
- "children"
68
- >;
69
-
70
- /**
71
- * Props to be passed to the internal primary <Button>
72
- */
73
- primaryButtonProps?: Omit<
74
- ComponentPropsWithRef<typeof Button>,
75
- keyof PrimaryButtonInternalProps
76
- >;
77
- }
78
-
79
- export type ConfirmDialogProps = Prettify<
80
- ConfirmDialogInternalProps &
81
- Omit<ComponentPropsWithRef<typeof Dialog>, keyof ConfirmDialogInternalProps>
82
- >;
83
-
84
- /**
85
- * ConfirmDialog component for displaying a simple confirmation dialog.
86
- *
87
- * @remarks
88
- * - Any property of Anvil's <Dialog> can be passed directly to <ConfirmDialog>
89
- *
90
- * - header, primaryButton, and cancelButton are typed as ReactNode,
91
- * so arbitrary React markup can be passed in addition to strings.
92
- *
93
- * - headerProps, contentProps, footerProps, cancelButtonProps, and primaryButtonProps
94
- * can be used to customize the internal components. They will be forwarded directly
95
- * to <Dialog.Header>, <Dialog.Content>, <Dialog.Footer>, etc.
96
- */
97
- export const ConfirmDialog: FC<ConfirmDialogProps> = ({
98
- appearance = "primary",
99
- header = "Confirm",
100
- children = "Are you sure?",
101
- primaryButton = "OK",
102
- cancelButton = "Cancel",
103
- onConfirm,
104
- cancelButtonProps,
105
- primaryButtonProps,
106
- headerProps,
107
- contentProps,
108
- footerProps,
109
- ...dialogProps
110
- }: ConfirmDialogProps) => {
111
- const primaryButtonInternalProps: PrimaryButtonInternalProps = {
112
- appearance,
113
- onClick: onConfirm,
114
- children: primaryButton,
115
- };
116
-
117
- return (
118
- <Dialog {...dialogProps}>
119
- <Dialog.Header {...headerProps}>{header}</Dialog.Header>
120
- <Dialog.Content {...contentProps}>{children}</Dialog.Content>
121
- <Dialog.Footer {...footerProps}>
122
- <Dialog.CancelButton {...cancelButtonProps}>
123
- {cancelButton}
124
- </Dialog.CancelButton>
125
-
126
- {/* allow primaryButtonProps object to override the explicit top-level props - primaryButton, appearance, onClick */}
127
- <Button {...primaryButtonInternalProps} {...primaryButtonProps} />
128
- </Dialog.Footer>
129
- </Dialog>
130
- );
131
- };
@@ -1,4 +0,0 @@
1
- export {
2
- useConfirmDialog,
3
- useConfirmDialog as default,
4
- } from "./useConfirmDialog";
@@ -1,177 +0,0 @@
1
- import type { Meta, StoryObj, StoryFn } from "@storybook/react-vite";
2
-
3
- import { action } from "storybook/actions";
4
- import { ComponentProps } from "react";
5
- import { Button, Flex } from "@servicetitan/anvil2";
6
- import { ConfirmDialog } from "./ConfirmDialog";
7
- import { useConfirmDialog } from "./useConfirmDialog";
8
-
9
- type UseConfirmDialogWrapperProps = Omit<
10
- ComponentProps<typeof ConfirmDialog>,
11
- "onCancel" | "onConfirm"
12
- > & {
13
- onConfirm: () => void;
14
- onCancel?: () => void;
15
- };
16
-
17
- const UseConfirmDialogWrapper = ({
18
- children,
19
- onConfirm,
20
- onCancel,
21
- ...rest
22
- }: UseConfirmDialogWrapperProps) => {
23
- const [ConfirmDialog, openConfirm] = useConfirmDialog(onConfirm, onCancel);
24
-
25
- return (
26
- <Flex justifyContent="center" alignItems="center" style={{ height: 400 }}>
27
- <Button onClick={() => openConfirm()}>Do Something</Button>
28
- <ConfirmDialog {...rest}>{children}</ConfirmDialog>
29
- </Flex>
30
- );
31
- };
32
-
33
- // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
34
- const meta = {
35
- title: "Hooks/UseConfirmDialog",
36
- component: UseConfirmDialogWrapper,
37
- parameters: {
38
- layout: "fullscreen",
39
- minHeight: "100dvh",
40
- },
41
- args: {
42
- header: undefined,
43
- children: undefined,
44
- primaryButton: undefined,
45
- primaryButtonProps: {},
46
- appearance: undefined,
47
- cancelButton: undefined,
48
- cancelButtonProps: {},
49
- headerProps: {},
50
- contentProps: {},
51
- footerProps: {},
52
- onConfirm: action("onConfirm"),
53
- onCancel: action("onCancel"),
54
- },
55
- argTypes: {
56
- header: {
57
- control: "text",
58
- type: "string",
59
- description: "The header content of the confirmation dialog",
60
- },
61
- children: {
62
- control: "text",
63
- type: "string",
64
- description: "The content of the confirmation dialog",
65
- },
66
- primaryButton: {
67
- control: "text",
68
- type: "string",
69
- description: "The content of the primary button",
70
- },
71
- primaryButtonProps: {
72
- control: "object",
73
- description:
74
- "Props to be passed to the primary button. See Button for details.",
75
- },
76
- appearance: {
77
- control: "select",
78
- options: ["primary", "secondary", "ghost", "danger", "danger-secondary"],
79
- description: "The appearance of the primary button",
80
- type: "string",
81
- },
82
- cancelButton: {
83
- control: "text",
84
- description: "The content of the cancel button",
85
- type: "string",
86
- },
87
- cancelButtonProps: {
88
- control: "object",
89
- description:
90
- "Props to be passed to the cancel button. See Button for details.",
91
- },
92
- headerProps: {
93
- control: "object",
94
- description:
95
- "Props to be passed to the header. See Dialog.Header for details.",
96
- },
97
- contentProps: {
98
- control: "object",
99
- description:
100
- "Props to be passed to the content. See Dialog.Content for details.",
101
- },
102
- footerProps: {
103
- control: "object",
104
- description:
105
- "Props to be passed to the footer. See Dialog.Footer for details.",
106
- },
107
- onConfirm: {
108
- action: "onConfirm",
109
- type: "function",
110
- description: "Callback fired when the primary button is clicked",
111
- },
112
- onCancel: {
113
- action: "onCancel",
114
- type: "function",
115
- description: "Callback fired when the cancel button is clicked",
116
- },
117
- },
118
-
119
- // More on argTypes: https://storybook.js.org/docs/api/argtypes
120
- } satisfies Meta<typeof UseConfirmDialogWrapper>;
121
-
122
- export default meta;
123
-
124
- const renderUseConfirmDialog: StoryFn<typeof UseConfirmDialogWrapper> = ({
125
- header,
126
- children,
127
- primaryButton,
128
- primaryButtonProps,
129
- appearance,
130
- cancelButton,
131
- cancelButtonProps,
132
- headerProps,
133
- contentProps,
134
- footerProps,
135
- onConfirm,
136
- onCancel,
137
- }) => {
138
- return (
139
- <UseConfirmDialogWrapper
140
- header={header}
141
- primaryButton={primaryButton}
142
- primaryButtonProps={primaryButtonProps}
143
- appearance={appearance}
144
- cancelButton={cancelButton}
145
- cancelButtonProps={cancelButtonProps}
146
- headerProps={headerProps}
147
- contentProps={contentProps}
148
- footerProps={footerProps}
149
- onConfirm={onConfirm}
150
- onCancel={onCancel}
151
- >
152
- {children}
153
- </UseConfirmDialogWrapper>
154
- );
155
- };
156
-
157
- export const UseConfirmDefaults: StoryFn<typeof UseConfirmDialogWrapper> =
158
- renderUseConfirmDialog;
159
-
160
- export const UseConfirmDanger: StoryObj<typeof UseConfirmDialogWrapper> = {
161
- args: {
162
- header: "Please Confirm",
163
- children: "Are you sure you want to do this?",
164
- primaryButton: "Yes, do it",
165
- primaryButtonProps: {},
166
- appearance: "danger",
167
- cancelButton: undefined,
168
- cancelButtonProps: {},
169
- headerProps: {},
170
- contentProps: {},
171
- footerProps: {},
172
- onConfirm: action("onConfirm"),
173
- onCancel: action("onCancel"),
174
- },
175
-
176
- render: renderUseConfirmDialog,
177
- };
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- /// <reference types="react/canary" />
2
-
3
- export * from "./hooks";
4
- export * from "./types";
@@ -1,7 +0,0 @@
1
- // Make a select list of properties optional
2
- export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
3
-
4
- // Improves display of complex types in Intellisense (VS Code and Visual Studio)
5
- export type Prettify<T> = {
6
- [K in keyof T]: T[K];
7
- } & {};
@@ -1,4 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "exclude": ["**/*.stories.*", "**/internal/**"]
4
- }
package/tsconfig.json DELETED
@@ -1,25 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES6",
4
- "useDefineForClassFields": true,
5
- "lib": ["ESNext", "DOM", "DOM.Iterable"],
6
- "module": "ESNext",
7
- "skipLibCheck": true,
8
-
9
- /* Bundler mode */
10
- "moduleResolution": "bundler",
11
- "allowImportingTsExtensions": true,
12
- "resolveJsonModule": true,
13
- "isolatedModules": true,
14
- "noEmit": true,
15
- "jsx": "react-jsx",
16
-
17
- /* Linting */
18
- "strict": true,
19
- "noUnusedLocals": true,
20
- "noUnusedParameters": true,
21
- "noFallthroughCasesInSwitch": true
22
- },
23
- "include": ["src"],
24
- "references": [{ "path": "./tsconfig.node.json" }]
25
- }
@@ -1,10 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "composite": true,
4
- "skipLibCheck": true,
5
- "module": "ESNext",
6
- "moduleResolution": "bundler",
7
- "allowSyntheticDefaultImports": true
8
- },
9
- "include": ["vite.config.ts"]
10
- }
package/vite.config.ts DELETED
@@ -1,126 +0,0 @@
1
- /// <reference types="vitest/config" />
2
- import { defineConfig } from "vite";
3
- import react from "@vitejs/plugin-react";
4
- import dts from "vite-plugin-dts";
5
- import { libInjectCss } from "vite-plugin-lib-inject-css";
6
- import svgr from "vite-plugin-svgr";
7
- import { readdirSync } from "node:fs";
8
- import packageJson from "./package.json";
9
-
10
- import path from "node:path";
11
- import { fileURLToPath } from "node:url";
12
- import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
13
-
14
- const dirname =
15
- typeof __dirname !== "undefined"
16
- ? __dirname
17
- : path.dirname(fileURLToPath(import.meta.url));
18
-
19
- const entries = ["hooks", "types"]
20
- .map((folderName) =>
21
- readdirSync(path.resolve(__dirname, `src/${folderName}`), {
22
- withFileTypes: true,
23
- })
24
- .filter((dirent) => dirent.isDirectory())
25
- .reduce(
26
- (acc, dirent) => ({
27
- ...acc,
28
- [`${dirent.name}`]: `src/${folderName}/${dirent.name}/index.ts`,
29
- }),
30
- {},
31
- ),
32
- )
33
- .reduce((acc, folderEntries) => ({ ...acc, ...folderEntries }), {});
34
-
35
- export default defineConfig({
36
- plugins: [
37
- react(),
38
- libInjectCss(),
39
- svgr({
40
- include: "**/*.svg",
41
- svgrOptions: {
42
- icon: true,
43
- },
44
- }),
45
- dts({
46
- entryRoot: "src",
47
- tsconfigPath: path.join(__dirname, "tsconfig.json"),
48
- exclude: ["**/*.test.tsx", "**/*.test.ts", "**/*.stories.tsx"],
49
- insertTypesEntry: true,
50
- }),
51
- ],
52
- build: {
53
- target: "esnext",
54
- minify: false,
55
- cssMinify: false,
56
- outDir: "./dist",
57
- emptyOutDir: true,
58
- reportCompressedSize: true,
59
- sourcemap: true,
60
- commonjsOptions: {
61
- transformMixedEsModules: true,
62
- },
63
- lib: {
64
- entry: {
65
- ...entries,
66
- index: "src/index.ts",
67
- },
68
- formats: ["es"],
69
- },
70
- rollupOptions: {
71
- external: [
72
- ...Object.keys(packageJson.peerDependencies),
73
- "@servicetitan/anvil2",
74
- /react\/jsx-runtime/,
75
- ],
76
- },
77
- },
78
- css: {
79
- postcss: "../../",
80
- },
81
- test: {
82
- environment: "happy-dom",
83
- exclude: ["**/node_modules/**"],
84
- css: {
85
- modules: {
86
- classNameStrategy: "non-scoped",
87
- },
88
- },
89
- projects: [
90
- {
91
- extends: true,
92
- test: {
93
- name: "unit",
94
- setupFiles: ["vitest-setup.ts"],
95
- include: ["**/*.test.ts", "**/*.test.tsx"],
96
- // coverage: {
97
- // provider: "istanbul",
98
- // include: ["src/**"],
99
- // exclude: ["**/*.stories.tsx", "**/*.test.ts", "**/*.test.tsx"],
100
- // reporter: ["text", "json", "html"],
101
- // },
102
- },
103
- },
104
- {
105
- extends: true,
106
- plugins: [
107
- // The plugin will run tests for the stories defined in your Storybook config
108
- // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
109
- storybookTest({
110
- configDir: path.join(dirname, ".storybook"),
111
- }),
112
- ],
113
- test: {
114
- name: "storybook",
115
- browser: {
116
- enabled: true,
117
- headless: true,
118
- provider: "playwright",
119
- instances: [{ browser: "chromium" }],
120
- },
121
- setupFiles: [".storybook/vitest.setup.ts"],
122
- },
123
- },
124
- ],
125
- },
126
- });
package/vitest-setup.ts DELETED
@@ -1,46 +0,0 @@
1
- import * as axeMatchers from "vitest-axe/matchers";
2
- import matchers from "@testing-library/jest-dom/matchers";
3
- import { vi, afterEach, beforeAll, expect } from "vitest";
4
- import "vitest-axe/extend-expect";
5
- import { cleanup } from "@testing-library/react";
6
-
7
- expect.extend(matchers);
8
- expect.extend(axeMatchers);
9
-
10
- afterEach(() => {
11
- cleanup();
12
- });
13
-
14
- beforeAll(() => {
15
- HTMLDialogElement.prototype.showModal = vi.fn();
16
- HTMLDialogElement.prototype.close = vi.fn();
17
- HTMLElement.prototype.showPopover = vi.fn();
18
- HTMLElement.prototype.hidePopover = vi.fn();
19
- });
20
-
21
- /**
22
- * mocking window.matchMedia in JSDOM
23
- * https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom
24
- *
25
- */
26
- Object.defineProperty(window, "matchMedia", {
27
- writable: true,
28
- value: vi.fn().mockImplementation((query) => ({
29
- matches: false,
30
- media: query,
31
- onchange: null,
32
- addListener: vi.fn(), // deprecated
33
- removeListener: vi.fn(), // deprecated
34
- addEventListener: vi.fn(),
35
- removeEventListener: vi.fn(),
36
- dispatchEvent: vi.fn(),
37
- })),
38
- });
39
-
40
- /**
41
- * Fixes a testing throw from axe-core
42
- * https://github.com/NickColley/jest-axe/issues/147
43
- *
44
- */
45
- const { getComputedStyle } = window;
46
- window.getComputedStyle = (elt) => getComputedStyle(elt);
package/vitest.shims.d.ts DELETED
@@ -1 +0,0 @@
1
- /// <reference types="@vitest/browser/providers/playwright" />
File without changes