@artsy/dismissible 0.1.0 → 0.3.0

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 ADDED
@@ -0,0 +1,31 @@
1
+ # v0.3.0 (Fri Dec 22 2023)
2
+
3
+ #### 🚀 Enhancement
4
+
5
+ - docs(readme): Add readme docs [#7](https://github.com/artsy/dismissible/pull/7) ([@damassi](https://github.com/damassi))
6
+
7
+ #### Authors: 1
8
+
9
+ - Christopher Pappas ([@damassi](https://github.com/damassi))
10
+
11
+ ---
12
+
13
+ # v0.2.0 (Wed Dec 20 2023)
14
+
15
+ #### 🚀 Enhancement
16
+
17
+ - fix(ci): Fixes NPM deploys and renames to @artsy/dismissible [#5](https://github.com/artsy/dismissible/pull/5) ([@damassi](https://github.com/damassi))
18
+ - feat(onboarding): Add and adapt code from force [#4](https://github.com/artsy/dismissible/pull/4) ([@damassi](https://github.com/damassi))
19
+
20
+ #### 🐛 Bug Fix
21
+
22
+ - chore(lib): 0.1.0 [#6](https://github.com/artsy/dismissible/pull/6) ([@damassi](https://github.com/damassi))
23
+ - chore(setup): Initial file addition [#1](https://github.com/artsy/dismissible/pull/1) ([@damassi](https://github.com/damassi))
24
+
25
+ #### ⚠️ Pushed to `main`
26
+
27
+ - Initial commit ([@damassi](https://github.com/damassi))
28
+
29
+ #### Authors: 1
30
+
31
+ - Christopher Pappas ([@damassi](https://github.com/damassi))
package/README.md CHANGED
@@ -2,11 +2,80 @@
2
2
 
3
3
  A package that stores dismissible key/value entries in localStorage, which can be used in apps that require progressive onboarding and the like.
4
4
 
5
- ## Use
5
+ ## Setup and Use
6
6
 
7
- TODO
7
+ ```bash
8
+ yarn install @artsy/dismissible
9
+ ```
10
+
11
+ Once the package is installed, add the `DismissibleProvider` context to your app, pass in localStorage `keys` to be dismissed later, and (optionally) add a `userID` to attach the key identifiers to:
12
+
13
+ ```jsx
14
+ const Root = () => {
15
+ return (
16
+ <DismissibleProvider
17
+ keys={["newFeature", "newFeature2"]}
18
+ userID="some-user-id"
19
+ >
20
+ <NewFeatureAlert>
21
+ <NewFeature />
22
+ </NewFeatureAlert>
23
+
24
+ <NewFeature2Alert>
25
+ <NewFeature2>
26
+ </NewFeature2Alert>
27
+ </DismissibleProvider>
28
+ )
29
+ }
30
+ ```
31
+
32
+ Then from `<NewFeatureAlert />`, can manage dismissible keys like so:
33
+
34
+ ```jsx
35
+ const NewFeatureAlert = () => {
36
+ const { dismiss, isDismissed } = useDismissibleContext()
37
+
38
+ const isDisplayable = !isDismissed("newFeature").status
39
+
40
+ const handleClose = () => {
41
+ dismiss(ALERT_ID)
42
+ }
43
+
44
+ if (!isDisplayable) {
45
+ return <>{children}</>
46
+ }
47
+
48
+ return <Popover message="Check out this new feature!">{children}</Popover>
49
+ }
50
+ ```
8
51
 
9
- ## Commands
52
+ ## API
53
+
54
+ The `useDismissibleContext` hook returns a few useful things for managing dismissibles:
55
+
56
+ ```jsx
57
+ const App = () => {
58
+ const { dismiss, dismissed, isDismissed, keys, syncFromLoggedOutUser } =
59
+ useDismissibleContext()
60
+
61
+ // Dismisses a key
62
+ dismiss("id")
63
+
64
+ // All dismissed keys
65
+ dismissed()
66
+
67
+ // Status of the thing dismissed, including timestamp
68
+ isDismissied("id")
69
+
70
+ // If a logged-out user logs in, resync so that dismissed keys don't reappear
71
+ syncFromLoggedOutUser()
72
+
73
+ // All dismissible keys
74
+ console.log(keys)
75
+ }
76
+ ```
77
+
78
+ ## Development
10
79
 
11
80
  ```bash
12
81
  yarn test
@@ -15,3 +84,20 @@ yarn lint
15
84
  yarn compile
16
85
  yarn watch
17
86
  ```
87
+
88
+ This project uses [auto-release](https://github.com/intuit/auto-release#readme)
89
+ to automatically release on every PR. Every PR should have a label that matches
90
+ one of the following
91
+
92
+ - Version: Trivial
93
+ - Version: Patch
94
+ - Version: Minor
95
+ - Version: Major
96
+
97
+ Major, minor, and patch will cause a new release to be generated. Use major for
98
+ breaking changes, minor for new non-breaking features, and patch for bug fixes.
99
+ Trivial will not cause a release and should be used when updating documentation
100
+ or non-project code.
101
+
102
+ If you don't want to release on a particular PR but the changes aren't trivial
103
+ then use the `Skip Release` tag along side the appropriate version tag.
@@ -0,0 +1,47 @@
1
+ import * as Yup from "yup";
2
+ import React from "react";
3
+ declare type DismissibleKey = DismissibleContextProps["keys"][number];
4
+ declare type DismissibleKeys = DismissibleKey[];
5
+ interface DismissedKey {
6
+ key: DismissibleKey;
7
+ timestamp: number;
8
+ }
9
+ interface DismissedKeyStatus {
10
+ status: boolean;
11
+ timestamp: number;
12
+ }
13
+ interface DismissibleContextProps {
14
+ dismissed: DismissedKey[];
15
+ dismiss: (key: DismissibleKey | readonly DismissibleKey[]) => void;
16
+ isDismissed: (key: DismissibleKey) => DismissedKeyStatus;
17
+ keys: string[];
18
+ syncFromLoggedOutUser: () => void;
19
+ /** An optional userID to track against */
20
+ userID?: string | null;
21
+ }
22
+ export declare const DismissibleProvider: React.FC<{
23
+ children: React.ReactNode;
24
+ keys: DismissibleKeys;
25
+ userID?: DismissibleContextProps["userID"];
26
+ }>;
27
+ export declare const useDismissibleContext: () => DismissibleContextProps;
28
+ export declare const DISMISSIBLE_LOGGED_OUT_USER_ID: "user";
29
+ export declare const localStorageKey: (id: string) => string;
30
+ interface UseLocalStorageUtilsProps {
31
+ keys: DismissibleContextProps["keys"];
32
+ }
33
+ export declare const useLocalStorageUtils: ({ keys }: UseLocalStorageUtilsProps) => {
34
+ __dismiss__: (id: string, timestamp: number, key: DismissibleKey | DismissibleKey[]) => void;
35
+ get: (id: string) => DismissedKey[];
36
+ isValid: (value: DismissedKey) => value is DismissedKey;
37
+ parse: (value: string | null) => DismissedKey[];
38
+ reset: (id: string) => void;
39
+ schema: Yup.ObjectSchema<{
40
+ key: string | undefined;
41
+ timestamp: number | undefined;
42
+ }, Yup.AnyObject, {
43
+ key: undefined;
44
+ timestamp: undefined;
45
+ }, "">;
46
+ };
47
+ export {};
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.useLocalStorageUtils = exports.localStorageKey = exports.DISMISSIBLE_LOGGED_OUT_USER_ID = exports.useDismissibleContext = exports.DismissibleProvider = void 0;
30
+ const jsx_runtime_1 = require("react/jsx-runtime");
31
+ const Yup = __importStar(require("yup"));
32
+ const react_1 = require("react");
33
+ const lodash_uniqby_1 = __importDefault(require("lodash.uniqby"));
34
+ const DismissibleContext = (0, react_1.createContext)({
35
+ dismissed: [],
36
+ keys: [],
37
+ isDismissed: () => ({ status: false, timestamp: 0 }),
38
+ });
39
+ const DismissibleProvider = ({ children, keys = [], userID }) => {
40
+ const id = userID !== null && userID !== void 0 ? userID : exports.DISMISSIBLE_LOGGED_OUT_USER_ID;
41
+ const [dismissed, setDismissed] = (0, react_1.useState)([]);
42
+ const localStorageUtils = (0, exports.useLocalStorageUtils)({ keys });
43
+ const { __dismiss__, get } = localStorageUtils;
44
+ const dismiss = (0, react_1.useCallback)((key) => {
45
+ const keys = Array.isArray(key) ? key : [key];
46
+ const timestamp = Date.now();
47
+ __dismiss__(id, timestamp, keys);
48
+ setDismissed((prevDismissed) => {
49
+ return (0, lodash_uniqby_1.default)([...prevDismissed, ...keys.map((k) => ({ key: k, timestamp }))], (d) => d.key);
50
+ });
51
+ }, [id]);
52
+ (0, react_1.useEffect)(() => {
53
+ setDismissed(get(id));
54
+ }, [id]);
55
+ const mounted = useDidMount();
56
+ const isDismissed = (0, react_1.useCallback)((key) => {
57
+ if (!mounted) {
58
+ return {
59
+ status: false,
60
+ timestamp: 0,
61
+ };
62
+ }
63
+ const dismissedKey = dismissed.find((d) => d.key === key);
64
+ return dismissedKey
65
+ ? { status: true, timestamp: dismissedKey.timestamp }
66
+ : { status: false, timestamp: 0 };
67
+ }, [dismissed, mounted]);
68
+ /**
69
+ * If the user is logged out, and performs some action which causes them
70
+ * to login, we need to sync up the dismissed state from the logged out user
71
+ */
72
+ const syncFromLoggedOutUser = (0, react_1.useCallback)(() => {
73
+ if (id === exports.DISMISSIBLE_LOGGED_OUT_USER_ID) {
74
+ return;
75
+ }
76
+ const loggedOutDismissals = get(exports.DISMISSIBLE_LOGGED_OUT_USER_ID);
77
+ const loggedInDismissals = get(id);
78
+ const dismissals = (0, lodash_uniqby_1.default)([...loggedOutDismissals, ...loggedInDismissals], (d) => d.key);
79
+ setDismissed(dismissals);
80
+ localStorage.setItem((0, exports.localStorageKey)(id), JSON.stringify(dismissals));
81
+ localStorage.removeItem((0, exports.localStorageKey)(exports.DISMISSIBLE_LOGGED_OUT_USER_ID));
82
+ }, [id]);
83
+ // Ensure that the dismissed state stays in sync incase the user
84
+ // has multiple tabs open.
85
+ (0, react_1.useEffect)(() => {
86
+ const current = get(id);
87
+ if (current.length === 0)
88
+ return;
89
+ const handleFocus = () => {
90
+ setDismissed(current);
91
+ };
92
+ window.addEventListener("focus", handleFocus);
93
+ return () => {
94
+ window.removeEventListener("focus", handleFocus);
95
+ };
96
+ }, [id]);
97
+ return ((0, jsx_runtime_1.jsx)(DismissibleContext.Provider, Object.assign({ value: {
98
+ dismissed,
99
+ dismiss,
100
+ isDismissed,
101
+ keys,
102
+ syncFromLoggedOutUser,
103
+ } }, { children: children })));
104
+ };
105
+ exports.DismissibleProvider = DismissibleProvider;
106
+ const useDismissibleContext = () => {
107
+ return (0, react_1.useContext)(DismissibleContext);
108
+ };
109
+ exports.useDismissibleContext = useDismissibleContext;
110
+ exports.DISMISSIBLE_LOGGED_OUT_USER_ID = "user";
111
+ const localStorageKey = (id) => {
112
+ return `progressive-onboarding.dismissed.${id}`;
113
+ };
114
+ exports.localStorageKey = localStorageKey;
115
+ const useLocalStorageUtils = ({ keys }) => {
116
+ const schema = Yup.object().shape({
117
+ key: Yup.string().oneOf([...keys]),
118
+ timestamp: Yup.number(),
119
+ });
120
+ const isValid = (value) => {
121
+ return schema.isValidSync(value);
122
+ };
123
+ const parse = (value) => {
124
+ if (!value)
125
+ return [];
126
+ try {
127
+ const parsed = JSON.parse(value);
128
+ return parsed.filter((obj) => {
129
+ return isValid(obj) && keys.includes(obj.key);
130
+ });
131
+ }
132
+ catch (err) {
133
+ return [];
134
+ }
135
+ };
136
+ const __dismiss__ = (id, timestamp, key) => {
137
+ const keys = Array.isArray(key) ? key : [key];
138
+ keys.forEach((key) => {
139
+ const item = localStorage.getItem((0, exports.localStorageKey)(id));
140
+ const dismissed = parse(item);
141
+ localStorage.setItem((0, exports.localStorageKey)(id), JSON.stringify((0, lodash_uniqby_1.default)([...dismissed, { key, timestamp }], ({ key }) => key)));
142
+ });
143
+ };
144
+ const get = (id) => {
145
+ const item = localStorage.getItem((0, exports.localStorageKey)(id));
146
+ return parse(item);
147
+ };
148
+ const reset = (id) => {
149
+ localStorage.removeItem((0, exports.localStorageKey)(id));
150
+ };
151
+ return {
152
+ __dismiss__,
153
+ get,
154
+ isValid,
155
+ parse,
156
+ reset,
157
+ schema,
158
+ };
159
+ };
160
+ exports.useLocalStorageUtils = useLocalStorageUtils;
161
+ function useDidMount(defaultMounted = false) {
162
+ const [isMounted, toggleMounted] = (0, react_1.useState)(defaultMounted);
163
+ (0, react_1.useEffect)(() => {
164
+ toggleMounted(true);
165
+ }, []);
166
+ return isMounted;
167
+ }
168
+ //# sourceMappingURL=DismissibleContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DismissibleContext.js","sourceRoot":"","sources":["../src/DismissibleContext.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { DismissibleProvider, useDismissibleContext, } from "./DismissibleContext";
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2DAG6B;AAF3B,yHAAA,mBAAmB,OAAA;AACnB,2HAAA,qBAAqB,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artsy/dismissible",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "license": "MIT",
@@ -8,8 +8,8 @@ import React, {
8
8
  } from "react"
9
9
  import uniqBy from "lodash.uniqby"
10
10
 
11
- export type DismissibleKey = DismissibleContextProps["keys"][number]
12
- export type DismissibleKeys = DismissibleKey[]
11
+ type DismissibleKey = DismissibleContextProps["keys"][number]
12
+ type DismissibleKeys = DismissibleKey[]
13
13
 
14
14
  interface DismissedKey {
15
15
  key: DismissibleKey
@@ -21,7 +21,7 @@ interface DismissedKeyStatus {
21
21
  timestamp: number
22
22
  }
23
23
 
24
- export interface DismissibleContextProps {
24
+ interface DismissibleContextProps {
25
25
  dismissed: DismissedKey[]
26
26
  dismiss: (key: DismissibleKey | readonly DismissibleKey[]) => void
27
27
  isDismissed: (key: DismissibleKey) => DismissedKeyStatus
@@ -29,10 +29,9 @@ export interface DismissibleContextProps {
29
29
  syncFromLoggedOutUser: () => void
30
30
  /** An optional userID to track against */
31
31
  userID?: string | null
32
- __internal__: ReturnType<typeof useLocalStorageUtils>
33
32
  }
34
33
 
35
- export const DismissibleContext = createContext<DismissibleContextProps>({
34
+ const DismissibleContext = createContext<DismissibleContextProps>({
36
35
  dismissed: [],
37
36
  keys: [],
38
37
  isDismissed: () => ({ status: false, timestamp: 0 }),
@@ -43,7 +42,7 @@ export const DismissibleProvider: React.FC<{
43
42
  keys: DismissibleKeys
44
43
  userID?: DismissibleContextProps["userID"]
45
44
  }> = ({ children, keys = [], userID }) => {
46
- const id = userID ?? PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID
45
+ const id = userID ?? DISMISSIBLE_LOGGED_OUT_USER_ID
47
46
 
48
47
  const [dismissed, setDismissed] = useState<DismissedKey[]>([])
49
48
 
@@ -96,11 +95,11 @@ export const DismissibleProvider: React.FC<{
96
95
  * to login, we need to sync up the dismissed state from the logged out user
97
96
  */
98
97
  const syncFromLoggedOutUser = useCallback(() => {
99
- if (id === PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID) {
98
+ if (id === DISMISSIBLE_LOGGED_OUT_USER_ID) {
100
99
  return
101
100
  }
102
101
 
103
- const loggedOutDismissals = get(PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID)
102
+ const loggedOutDismissals = get(DISMISSIBLE_LOGGED_OUT_USER_ID)
104
103
  const loggedInDismissals = get(id)
105
104
 
106
105
  const dismissals = uniqBy(
@@ -111,9 +110,7 @@ export const DismissibleProvider: React.FC<{
111
110
  setDismissed(dismissals)
112
111
 
113
112
  localStorage.setItem(localStorageKey(id), JSON.stringify(dismissals))
114
- localStorage.removeItem(
115
- localStorageKey(PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID)
116
- )
113
+ localStorage.removeItem(localStorageKey(DISMISSIBLE_LOGGED_OUT_USER_ID))
117
114
  }, [id])
118
115
 
119
116
  // Ensure that the dismissed state stays in sync incase the user
@@ -142,7 +139,6 @@ export const DismissibleProvider: React.FC<{
142
139
  isDismissed,
143
140
  keys,
144
141
  syncFromLoggedOutUser,
145
- __internal__: localStorageUtils,
146
142
  }}
147
143
  >
148
144
  {children}
@@ -154,7 +150,7 @@ export const useDismissibleContext = () => {
154
150
  return useContext(DismissibleContext)
155
151
  }
156
152
 
157
- export const PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID = "user" as const
153
+ export const DISMISSIBLE_LOGGED_OUT_USER_ID = "user" as const
158
154
 
159
155
  export const localStorageKey = (id: string) => {
160
156
  return `progressive-onboarding.dismissed.${id}`
@@ -1,7 +1,7 @@
1
1
  import React from "react"
2
2
  import { renderHook } from "@testing-library/react-hooks"
3
3
  import {
4
- PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID,
4
+ DISMISSIBLE_LOGGED_OUT_USER_ID,
5
5
  DismissibleProvider,
6
6
  useLocalStorageUtils,
7
7
  useDismissibleContext,
@@ -230,7 +230,7 @@ describe("DismissibleContext", () => {
230
230
 
231
231
  describe("logged in", () => {
232
232
  it("syncs the dismissed state from the logged out user", () => {
233
- const loggedOutUserId = PROGRESSIVE_ONBOARDING_LOGGED_OUT_USER_ID
233
+ const loggedOutUserId = DISMISSIBLE_LOGGED_OUT_USER_ID
234
234
 
235
235
  const loggedOutDismissals = [
236
236
  { key: "follow-artist", timestamp: 555 },