diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index c9c04d3..b7ab02b 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -11,12 +11,13 @@ jobs: runs-on: ubuntu-latest # Job steps steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - run: yarn + - name: Install dependencies + run: yarn #👇 Adds Chromatic as a step in the workflow - - uses: chromaui/action@v1 + - uses: chromaui/action@latest # Options required for Chromatic's GitHub Action with: #👇 Chromatic projectToken, see https://storybook.js.org/tutorials/intro-to-storybook/react/en/deploy/ to obtain it diff --git a/.storybook/main.ts b/.storybook/main.ts index 22348c5..0671ce4 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -13,8 +13,6 @@ const config: StorybookConfig = { name: '@storybook/react-vite', options: {}, }, - docs: { - autodocs: 'tag', - }, + docs: {}, } export default config diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 4d55228..9b14322 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/index.html b/index.html index 79c4701..22365a1 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,6 @@
- + diff --git a/package.json b/package.json index ce86259..ec17113 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,17 @@ "storybook": "storybook dev -p 6006", "test-storybook": "vitest --project=storybook" }, + "imports": { + "#*": [ + "./src/*", + "./src/*.ts", + "./src/*.tsx" + ], + "#utils/date": { + "storybook": "./src/utils/date.mock.ts", + "default": "./src/utils/date.ts" + } + }, "dependencies": { "@reduxjs/toolkit": "^2.0.1", "react": "^18.2.0", @@ -30,13 +41,13 @@ }, "devDependencies": { "@chromatic-com/storybook": "^3.2.2", - "@storybook/addon-a11y": "^8.4.0", - "@storybook/addon-essentials": "^8.4.0", - "@storybook/blocks": "^8.4.0", - "@storybook/experimental-addon-test": "^8.4.4", - "@storybook/react": "^8.4.0", - "@storybook/react-vite": "^8.4.0", - "@storybook/test": "^8.4.0", + "@storybook/addon-a11y": "^8.5.0-alpha.9", + "@storybook/addon-essentials": "^8.5.0-alpha.9", + "@storybook/blocks": "^8.5.0-alpha.9", + "@storybook/experimental-addon-test": "^8.5.0-alpha.9", + "@storybook/react": "^8.5.0-alpha.9", + "@storybook/react-vite": "^8.5.0-alpha.9", + "@storybook/test": "^8.5.0-alpha.9", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", "@vitejs/plugin-react": "^4.2.1", @@ -48,11 +59,12 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-storybook": "^0.8.0", + "mockdate": "^3.0.5", "msw": "^2.3.0", "msw-storybook-addon": "^2.0.3", "playwright": "^1.49.0", "prop-types": "^15.8.1", - "storybook": "^8.4.0", + "storybook": "^8.5.0-alpha.9", "ts-migrate": "^0.1.35", "typescript": "^5.6.3", "vite": "^5.2.0", @@ -60,6 +72,5 @@ }, "msw": { "workerDirectory": "public" - }, - "packageManager": "yarn@4.5.1" + } } diff --git a/src/components/InboxScreen.stories.tsx b/src/components/InboxScreen.stories.tsx index 25ee4d9..47e53f3 100644 --- a/src/components/InboxScreen.stories.tsx +++ b/src/components/InboxScreen.stories.tsx @@ -1,10 +1,11 @@ import { Meta, StoryObj } from '@storybook/react' import { HttpResponse, http, delay } from 'msw' import { Provider } from 'react-redux' +import MockDate from 'mockdate' import store from '../lib/store' import InboxScreen from './InboxScreen' -import { MockedState } from './TaskList.stories' +import * as mocks from '../mocks/data' import { userEvent, @@ -12,6 +13,7 @@ import { within, waitForElementToBeRemoved, } from '@storybook/test' +import { getFormattedDate } from '#utils/date.mock.ts' const meta = { component: InboxScreen, @@ -29,12 +31,16 @@ export const Default: Story = { http.get( 'https://jsonplaceholder.typicode.com/todos', () => { - return HttpResponse.json(MockedState.tasks) + return HttpResponse.json(mocks.tasks) } ), ], }, }, +} + +export const PinnedTasks: Story = { + ...Default, play: async ({ canvasElement }) => { const canvas = within(canvasElement) // Waits for the component to transition from the loading state @@ -42,9 +48,9 @@ export const Default: Story = { // Waits for the component to be updated based on the store await waitFor(async () => { // Simulates pinning the first task - await userEvent.click(canvas.getByLabelText('pinTask-1')) + await userEvent.click(canvas.getByLabelText('Pin Learn more about Storybook')) // Simulates pinning the third task - await userEvent.click(canvas.getByLabelText('pinTask-3')) + await userEvent.click(canvas.getByLabelText('Pin Schedule annual health check-up')) }) }, } @@ -72,3 +78,17 @@ export const Loading: Story = { }, }, } + +export const MockedDateWithModuleMocking: Story = { + ...Default, + beforeEach: async () => { + getFormattedDate.mockReturnValue('August 23, 1993') + } +} + +export const MockedDateWithDateMocking: Story = { + ...Default, + beforeEach: async () => { + MockDate.set('2000-01-01T12:24:02Z') + } +} diff --git a/src/components/InboxScreen.tsx b/src/components/InboxScreen.tsx index 9df64c3..2afc04a 100644 --- a/src/components/InboxScreen.tsx +++ b/src/components/InboxScreen.tsx @@ -1,9 +1,10 @@ -import { useEffect } from "react"; +import { useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "../lib/hooks"; import { fetchTasks } from "../lib/store"; import { selectTaskbox } from "../lib/selectors"; import TaskList from "./TaskList"; +import { getFormattedDate } from "#utils/date"; export default function InboxScreen() { const dispatch = useDispatch(); @@ -14,6 +15,8 @@ export default function InboxScreen() { dispatch(fetchTasks()); }, []); + const today = useMemo(() => getFormattedDate(new Date()), []); + if (error) { return (
@@ -28,7 +31,7 @@ export default function InboxScreen() { return (
diff --git a/src/components/Task.stories.tsx b/src/components/Task.stories.tsx index d8bcc4d..3c36842 100644 --- a/src/components/Task.stories.tsx +++ b/src/components/Task.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryObj } from '@storybook/react' -import { fn } from '@storybook/test'; +import { expect, fn, userEvent } from '@storybook/test'; +import * as mocks from '../mocks/data' import Task from "./Task"; @@ -18,11 +19,7 @@ type Story = StoryObj export const Default: Story = { args: { - task: { - id: "1", - title: "Test Task", - state: "TASK_INBOX", - }, + task: mocks.task, }, }; @@ -44,13 +41,23 @@ export const Archived: Story = { }, }; -const longTitleString = `This task's name is absurdly large. In fact, I think if I keep going I might end up with content overflow. What will happen? The star that represents a pinned task could have text overlapping. The text could cut-off abruptly when it reaches the star. I hope not!`; - export const LongTitle: Story = { args: { task: { ...Default.args.task, - title: longTitleString, + title: `This task's name is absurdly large. In fact, I think if I keep going I might end up with content overflow. What will happen? The star that represents a pinned task could have text overlapping. The text could cut-off abruptly when it reaches the star. I hope not!`, }, }, }; + +export const Test: Story = { + ...Default, + play: async({ canvas, args }) => { + await userEvent.click(canvas.getByLabelText('Pin Learn more about Storybook')) + await expect(args.onPinTask).toHaveBeenCalledWith(mocks.task.id) + + await userEvent.click(canvas.getByLabelText('Archive Learn more about Storybook')) + await expect(args.onArchiveTask).toHaveBeenCalledWith(mocks.task.id) + }, + tags: ['!autodocs'] +} diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 929167f..faf0ce6 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -12,11 +12,12 @@ export default function TaskComponent({ onPinTask, }: TaskProps) { return ( -
+
  • {state !== "TASK_ARCHIVED" && ( @@ -44,12 +38,12 @@ export default function TaskComponent({ className="pin-button" onClick={() => onPinTask(id)} id={`pinTask-${id}`} - aria-label={`pinTask-${id}`} + aria-label={`Pin ${title}`} key={`pinTask-${id}`} > )} -
  • + ); } diff --git a/src/components/TaskList.stories.tsx b/src/components/TaskList.stories.tsx index 8444e47..9813284 100644 --- a/src/components/TaskList.stories.tsx +++ b/src/components/TaskList.stories.tsx @@ -1,23 +1,17 @@ -import { Meta, StoryObj } from '@storybook/react' +import type { ReactElement } from 'react'; +import type { Meta, StoryObj } from '@storybook/react' import { Provider } from "react-redux"; import { configureStore, createSlice } from "@reduxjs/toolkit"; import TaskList from "./TaskList"; -import * as TaskStories from "./Task.stories"; import { State } from '../lib/store'; -import { ReactElement } from 'react'; import { Task } from '../types'; +import * as mocks from "../mocks/data"; +import { expect, userEvent } from '@storybook/test'; // A super-simple mock of the state of the store -export const MockedState: State = { - tasks: [ - { ...TaskStories.Default.args.task, id: "1", title: "Task 1" }, - { ...TaskStories.Default.args.task, id: "2", title: "Task 2" }, - { ...TaskStories.Default.args.task, id: "3", title: "Task 3" }, - { ...TaskStories.Default.args.task, id: "4", title: "Task 4" }, - { ...TaskStories.Default.args.task, id: "5", title: "Task 5" }, - { ...TaskStories.Default.args.task, id: "6", title: "Task 6" }, - ], +const defaultTaskboxState: State = { + tasks: mocks.tasks, status: "idle", error: null, }; @@ -52,7 +46,6 @@ const meta = { title: "TaskList", tags: ["autodocs"], decorators: [(story) =>
    {story()}
    ], - excludeStories: /.*MockedState$/, } satisfies Meta export default meta; @@ -60,7 +53,7 @@ type Story = StoryObj export const Default: Story = { decorators: [ - (story) => {story()}, + (story) => {story()}, ], }; @@ -68,14 +61,14 @@ export const WithPinnedTasks: Story = { decorators: [ (story) => { const pinnedtasks: Task[] = [ - ...MockedState.tasks.slice(0, 5), + ...defaultTaskboxState.tasks.slice(0, 5), { id: "6", title: "Task 6 (pinned)", state: "TASK_PINNED" }, ]; return ( @@ -91,7 +84,7 @@ export const Loading: Story = { (story) => ( @@ -106,7 +99,7 @@ export const Empty: Story = { (story) => ( @@ -115,3 +108,29 @@ export const Empty: Story = { ), ], }; + +export const TestPinBehavior: Story = { + ...Default, + play: async ({ canvas, step }) => { + + await step('Ensure tasks are rendered in the initial order', async () => { + const listItems = canvas.getAllByRole("listitem"); + await expect(listItems[0]).toHaveTextContent("Learn more about Storybook"); + await expect(listItems[1]).toHaveTextContent("Go to the gym"); + }) + + await step('Pin "Go to the gym" task', async () => { + // Pin Learn more about Storybook and verify it moves to the top + const pinButton = canvas.getByLabelText("Pin Go to the gym"); + await userEvent.click(pinButton); + }); + + await step('Ensure tasks order is changed', async () => { + const updatedListItems = canvas.getAllByRole("listitem"); + await expect(updatedListItems[0]).toHaveTextContent("Go to the gym"); + await expect(updatedListItems[1]).toHaveTextContent("Learn more about Storybook"); + }); + }, + // hide the story from autodocs page as it's intended for test purposes only + tags: ['!autodocs'] +}; \ No newline at end of file diff --git a/src/components/TaskList.tsx b/src/components/TaskList.tsx index 464f776..29aab7f 100644 --- a/src/components/TaskList.tsx +++ b/src/components/TaskList.tsx @@ -19,29 +19,25 @@ export default function TaskList() { // We're dispatching the Archive event back to our store dispatch(updateTaskState({ id, newTaskState: "TASK_ARCHIVED" })); }; - const LoadingRow = ( -
    - - - Loading cool state - -
    - ); + if (status === "loading") { return ( -
    - {LoadingRow} - {LoadingRow} - {LoadingRow} - {LoadingRow} - {LoadingRow} - {LoadingRow} +
    + {Array.from({ length: 6 }).map((_, index) => ( +
    + + + Loading cool state + +
    + ))}
    ); } + if (tasks.length === 0) { return ( -
    +

    You have no tasks

    @@ -50,9 +46,8 @@ export default function TaskList() {
    ); } - return ( -
    +
      {tasks.map((task) => ( archiveTask(task)} /> ))} -
    + ); } diff --git a/src/index.css b/src/index.css index b121ab7..7eb0103 100644 --- a/src/index.css +++ b/src/index.css @@ -251,6 +251,11 @@ textarea { content: "\e65e"; } +ul { + padding: 0; + margin: 0; +} + .list-heading { letter-spacing: 0.3em; text-indent: 0.3em; @@ -287,6 +292,7 @@ textarea { text-overflow: ellipsis; white-space: nowrap; flex: 1; + align-content: center; } .list-item input[type="text"] { diff --git a/src/mocks/data.ts b/src/mocks/data.ts new file mode 100644 index 0000000..0e13a56 --- /dev/null +++ b/src/mocks/data.ts @@ -0,0 +1,16 @@ +import { Task } from '../types' + +export const task: Task = { + id: '1', + title: 'Learn more about Storybook', + state: 'TASK_INBOX', +} + +export const tasks: Task[] = [ + { ...task, id: '1', title: 'Learn more about Storybook' }, + { ...task, id: '2', title: 'Go to the gym' }, + { ...task, id: '3', title: 'Schedule annual health check-up' }, + { ...task, id: '4', title: 'Organize workspace and declutter' }, + { ...task, id: '5', title: 'Clean the house' }, + { ...task, id: '6', title: 'Prepare presentation slides for Monday' }, +] diff --git a/src/utils/date.mock.ts b/src/utils/date.mock.ts new file mode 100644 index 0000000..69adfea --- /dev/null +++ b/src/utils/date.mock.ts @@ -0,0 +1,6 @@ +import { fn } from '@storybook/test' +import * as date from './date' + +export const getFormattedDate = fn(date.getFormattedDate).mockName( + 'getFormattedDate' +) diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 0000000..47f8024 --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,8 @@ +export const getFormattedDate = (date: Date) => { + const formatted = date.toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + year: 'numeric', + }) + return formatted +} diff --git a/yarn.lock b/yarn.lock index 300f458..bcdb45c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2521,21 +2521,21 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-a11y@npm:^8.4.0": - version: 8.4.4 - resolution: "@storybook/addon-a11y@npm:8.4.4" +"@storybook/addon-a11y@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-a11y@npm:8.5.0-alpha.9" dependencies: - "@storybook/addon-highlight": "npm:8.4.4" + "@storybook/addon-highlight": "npm:8.5.0-alpha.9" axe-core: "npm:^4.2.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/19e530cdda82757667271334fac47e944d064e639d0e0b027c78052b0ebb385851d19fb650a868ef3e9c390576f6919e5141dbea8f9cbbaf881662c0ba83ef85 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/3a3ac08272316b6a057936ec59dad11243fe1132eff9140a3666197cb144118bebf2293399695bae1f5b13930080c6013fbe2f87ed2a44f4f0db4bf3877f0ad0 languageName: node linkType: hard -"@storybook/addon-actions@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-actions@npm:8.4.4" +"@storybook/addon-actions@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-actions@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" "@types/uuid": "npm:^9.0.1" @@ -2543,132 +2543,132 @@ __metadata: polished: "npm:^4.2.2" uuid: "npm:^9.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/ba4f310a047f3c0c8eed7ac61d47470dcd388e0bd584e1f17918c22e89fc060e06b403ceb7c591746b12c1fdbf796fe46fe5f797ee2ff69f5272b29c173979f8 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/d9d373fc7dadecd6d9a02f9889e381ee6125147253d495e2be6179769150e8980cf0cf8dca0ea69983715aa93bfdff7fd1959724fc9f78168cd1471a9a71a72f languageName: node linkType: hard -"@storybook/addon-backgrounds@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-backgrounds@npm:8.4.4" +"@storybook/addon-backgrounds@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-backgrounds@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" memoizerific: "npm:^1.11.3" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/9b728c4e7c75990b855d6838f57c483041e178ee917f97dcc204355884cc8c78ee76c635831c5f59fa33f59e359dab4b5aa05a22584145a3adbae87b1e6aa5bc + storybook: ^8.5.0-alpha.9 + checksum: 10c0/a24674e227f445d4c12e7f4dfb37b916ee0fefbba3f786f64678c28511f12636ee453c1fef25c28a2f7655f1a477c6a03dfdac408ad5163a5805d36c17b6bd4d languageName: node linkType: hard -"@storybook/addon-controls@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-controls@npm:8.4.4" +"@storybook/addon-controls@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-controls@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" dequal: "npm:^2.0.2" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/a7e0de38fee916193441a35e89e4a922e1beb6b295758fd39b88b31899e78a27651eb184cf1d36c98eda94865a3cc9d1484acef68c8c495f5602f2989d8f0495 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/b68788c529e12e06725a738c098e6db7030f7f575f2d6104625db0dcc84fed6850762267bd6fb3f0591aa4905b8eef3fcdea924c74ffbc8b6964ffe744872b4e languageName: node linkType: hard -"@storybook/addon-docs@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-docs@npm:8.4.4" +"@storybook/addon-docs@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-docs@npm:8.5.0-alpha.9" dependencies: "@mdx-js/react": "npm:^3.0.0" - "@storybook/blocks": "npm:8.4.4" - "@storybook/csf-plugin": "npm:8.4.4" - "@storybook/react-dom-shim": "npm:8.4.4" + "@storybook/blocks": "npm:8.5.0-alpha.9" + "@storybook/csf-plugin": "npm:8.5.0-alpha.9" + "@storybook/react-dom-shim": "npm:8.5.0-alpha.9" react: "npm:^16.8.0 || ^17.0.0 || ^18.0.0" react-dom: "npm:^16.8.0 || ^17.0.0 || ^18.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/121dbc3a04e3b7ebd210bc28f47cbc50dcc27dba7c7e749c846a51bc3a7c2142686e0e6deff1084fcf0eda081856b4b00563f22fa599b25b1d6bf87d3a595b54 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/4aa9ba896ba48630bed8e7e8fbcae7a1ed0693390edb46755d7ab9de4d637c4ce55b7b446c0fe986b1c09f5467c95b80bba1ab9ff693692acd2cbd34a0369560 languageName: node linkType: hard -"@storybook/addon-essentials@npm:^8.4.0": - version: 8.4.4 - resolution: "@storybook/addon-essentials@npm:8.4.4" +"@storybook/addon-essentials@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-essentials@npm:8.5.0-alpha.9" dependencies: - "@storybook/addon-actions": "npm:8.4.4" - "@storybook/addon-backgrounds": "npm:8.4.4" - "@storybook/addon-controls": "npm:8.4.4" - "@storybook/addon-docs": "npm:8.4.4" - "@storybook/addon-highlight": "npm:8.4.4" - "@storybook/addon-measure": "npm:8.4.4" - "@storybook/addon-outline": "npm:8.4.4" - "@storybook/addon-toolbars": "npm:8.4.4" - "@storybook/addon-viewport": "npm:8.4.4" + "@storybook/addon-actions": "npm:8.5.0-alpha.9" + "@storybook/addon-backgrounds": "npm:8.5.0-alpha.9" + "@storybook/addon-controls": "npm:8.5.0-alpha.9" + "@storybook/addon-docs": "npm:8.5.0-alpha.9" + "@storybook/addon-highlight": "npm:8.5.0-alpha.9" + "@storybook/addon-measure": "npm:8.5.0-alpha.9" + "@storybook/addon-outline": "npm:8.5.0-alpha.9" + "@storybook/addon-toolbars": "npm:8.5.0-alpha.9" + "@storybook/addon-viewport": "npm:8.5.0-alpha.9" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/b3e7f1f53a3a4866cccdee663667595450dd73bbcc530fe5baa7ec3650b5a1c9f364c5c1e97c08b85e5b918445d3977f63c7c79fe8c1295800e619e7aaf22c06 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/e1da721f82abb6c5649daf82f78e94dc568defe07cfa5b62fd208a4711beef5a04f246208423e1daf6af872cf921e02006b5d95290f805c245a0664c16d5ef0b languageName: node linkType: hard -"@storybook/addon-highlight@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-highlight@npm:8.4.4" +"@storybook/addon-highlight@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-highlight@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/d552b136d72776b8b596ccd2d40d7a1b013bbe43cc362a7989b61157109c490e90ab9496236ce0020633d3e034c2e84237907f6c6213b4c922e8739836f9be9d + storybook: ^8.5.0-alpha.9 + checksum: 10c0/d9585bc601d500b7401e337a281197053dfaef2a965b594088fc109ef3b355468c4fa03444059ccac7b54f2bb2bd5123afa0dc354b2dc4f1bce50630d182fd15 languageName: node linkType: hard -"@storybook/addon-measure@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-measure@npm:8.4.4" +"@storybook/addon-measure@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-measure@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" tiny-invariant: "npm:^1.3.1" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/9c3a1ec20ec3551f1803d44b26b60b62062e4086fe66e20fdf876a6b495d0ae0e5a8c5c446adab0de5c46963bfcc221bfb91b9a51446dbd811d1e0d7e316e53b + storybook: ^8.5.0-alpha.9 + checksum: 10c0/d59ad7493bf3706342d7165909b6ed2d5c62480ede6a1855eecb9a6f360f20f2a8b712870f7c979c4403fbcddfdb74724b25e2523571214336887edfd5d53f90 languageName: node linkType: hard -"@storybook/addon-outline@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-outline@npm:8.4.4" +"@storybook/addon-outline@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-outline@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/c502fa23366b83c5d28e5b1e80e9fc53c723aa608d6672bb6c25e6598b163b7a293b17ac33e73dc65655fcb6f56d2835f8c64f07cfc56dccbed86d45fdbc3fb2 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/db119dfefa463dd8e6a652bbae94294ff8a49dd866119add451cec4731346400ee76a95658245721be4951f87a1063373f90424f4737d4810003014338e86a31 languageName: node linkType: hard -"@storybook/addon-toolbars@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-toolbars@npm:8.4.4" +"@storybook/addon-toolbars@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-toolbars@npm:8.5.0-alpha.9" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/de81fe6326ebd9644ea4c6ae131b7c11d1e2e83eeefc2a33e41c72fa4b98e8367485a06f976458cb91cdd91a46bea6189990d67ded6d66293a4eea899f605272 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/b6f1ae0dafa7d9c2f2b6ed34f7f66791ff1d312a3e63513657457b22ac76e897117ce1c2e42b51959b4da98c74bfcade36f3279739acd9cefd7c1a9e8fc12dcd languageName: node linkType: hard -"@storybook/addon-viewport@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/addon-viewport@npm:8.4.4" +"@storybook/addon-viewport@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/addon-viewport@npm:8.5.0-alpha.9" dependencies: memoizerific: "npm:^1.11.3" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/de85220336d119fc1ff71fe78d54767985c1eb37cd4a03ccd43d5ccddd6f1d1e43aa73fb8f304eaa6e53c4a78234adc80482aa2f593ad272bc1389f1ec044805 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/352356a16f6082ea6effbb40107637325ecbf889ec051939ff89a6b05f2a95a4a69d9e073f5f67c19015c3686a544da14596e31820c2b35d95ec5552f4500810 languageName: node linkType: hard -"@storybook/blocks@npm:8.4.4, @storybook/blocks@npm:^8.4.0": - version: 8.4.4 - resolution: "@storybook/blocks@npm:8.4.4" +"@storybook/blocks@npm:8.5.0-alpha.9, @storybook/blocks@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/blocks@npm:8.5.0-alpha.9" dependencies: "@storybook/csf": "npm:^0.1.11" "@storybook/icons": "npm:^1.2.12" @@ -2676,42 +2676,42 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.4 + storybook: ^8.5.0-alpha.9 peerDependenciesMeta: react: optional: true react-dom: optional: true - checksum: 10c0/6d32ade4acbe3eec89010af8093ee09ed1a74ddc3126ba1daa03d58e86a09f57df73de29432e7635e536a383a496002d9142543178825a665cd58f313731c364 + checksum: 10c0/15194abcfdb6302e9a7db9009e1395eb08268fbf70c4f071bd9559bacbb87f32ba4a0ffa2dd62e560de4f9a6df8b78b78604f68245a7ff94ae30f4d29c03399c languageName: node linkType: hard -"@storybook/builder-vite@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/builder-vite@npm:8.4.4" +"@storybook/builder-vite@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/builder-vite@npm:8.5.0-alpha.9" dependencies: - "@storybook/csf-plugin": "npm:8.4.4" + "@storybook/csf-plugin": "npm:8.5.0-alpha.9" browser-assert: "npm:^1.2.1" ts-dedent: "npm:^2.0.0" peerDependencies: - storybook: ^8.4.4 + storybook: ^8.5.0-alpha.9 vite: ^4.0.0 || ^5.0.0 - checksum: 10c0/f5fd27b048930fe17db4bd90becaee9980d952998149d1e9fce7cdb273898eb2d954787c9e7bb3829f9858e1a2503fce02afce67d7941f9b996f5363e9f3d063 + checksum: 10c0/7ff266ba14e0c265dd21adee79e814355b978f4463b2f6ebf30e3ed79ba89695a01f9095b6781be7c4500d4c370efac63d104850782b5c62a019f2a6d683a491 languageName: node linkType: hard -"@storybook/components@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/components@npm:8.4.4" +"@storybook/components@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/components@npm:8.5.0-alpha.9" peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10c0/fd87c6e38d62a72256fea85a3ff4a865c5f8357dc02ee6a0f09be8e9897c0bfbc886460126b1d277e8642d438241ad76f83d01035278989eac6f6a0c23f13f5a + checksum: 10c0/5a7c6fa7b83c5940fbe452e1a453400b32c8a333fc9e75efe4e66e3177d96e2e80b2808ce0188161608b7f6bb5a2a3634e7be6fcb98127d8131fd065b4371fdf languageName: node linkType: hard -"@storybook/core@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/core@npm:8.4.4" +"@storybook/core@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/core@npm:8.5.0-alpha.9" dependencies: "@storybook/csf": "npm:^0.1.11" better-opn: "npm:^3.0.2" @@ -2729,18 +2729,18 @@ __metadata: peerDependenciesMeta: prettier: optional: true - checksum: 10c0/101bb9c13bc339572c071215bb9d2e5a7c514c67f6957dba1d0ed28ec993d6b1f8bd0d367fb3b49c80b5489131c744647878f6701695b868dfd5dbd73581a82c + checksum: 10c0/4b9856c69a49b5ee53ee72e387ba37af18abadd5400968fe64491b94a1e700e9ce94c49a3ec8045b57814a3f6591054f2cf3cd26d0eb78d55fc3bc01e27c31fe languageName: node linkType: hard -"@storybook/csf-plugin@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/csf-plugin@npm:8.4.4" +"@storybook/csf-plugin@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/csf-plugin@npm:8.5.0-alpha.9" dependencies: unplugin: "npm:^1.3.1" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/d62e1288b0ed900af8f3c617496ceb908ffafd4b6cf7fc97c5e008cfa153d04245a18c9a008d6a59beb7720bbc88f272224dc19e2425db4b0d5d88ac38d2a885 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/2e24b5beed81c3c75478d168070eac2a8ab9f29b8b562980b80ab16270c7b20835a1054409506516798bf1649328847c056b8e066aa82c94912a61b395bc6a31 languageName: node linkType: hard @@ -2762,23 +2762,23 @@ __metadata: languageName: node linkType: hard -"@storybook/experimental-addon-test@npm:^8.4.4": - version: 8.4.4 - resolution: "@storybook/experimental-addon-test@npm:8.4.4" +"@storybook/experimental-addon-test@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/experimental-addon-test@npm:8.5.0-alpha.9" dependencies: "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.12" - "@storybook/instrumenter": "npm:8.4.4" - "@storybook/test": "npm:8.4.4" - "@storybook/theming": "npm:8.4.4" + "@storybook/instrumenter": "npm:8.5.0-alpha.9" + "@storybook/test": "npm:8.5.0-alpha.9" + "@storybook/theming": "npm:8.5.0-alpha.9" polished: "npm:^4.2.2" prompts: "npm:^2.4.0" ts-dedent: "npm:^2.2.0" peerDependencies: "@vitest/browser": ^2.1.1 "@vitest/runner": ^2.1.1 - storybook: ^8.4.4 + storybook: ^8.5.0-alpha.9 vitest: ^2.1.1 peerDependenciesMeta: "@vitest/browser": @@ -2787,7 +2787,7 @@ __metadata: optional: true vitest: optional: true - checksum: 10c0/448583bec68b08e7cb006db796ec4df4f1ac64d68edb5799d8b299cea4ebd2d560e8e674e98936ddc209a999bef7edbadb50f08b356905976fa416c54994863b + checksum: 10c0/0a99b2135658cf5531ee214ac4da6d55c851ea21fd64f97214c47aa227b3e6e35dd385cfcbbc29f545f15ffa6ece5d3e748be878aa0caab0de495765b1b1f670 languageName: node linkType: hard @@ -2808,55 +2808,55 @@ __metadata: languageName: node linkType: hard -"@storybook/instrumenter@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/instrumenter@npm:8.4.4" +"@storybook/instrumenter@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/instrumenter@npm:8.5.0-alpha.9" dependencies: "@storybook/global": "npm:^5.0.0" "@vitest/utils": "npm:^2.1.1" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/75045f4f52675b0014f34a73aeb8636a42e2ebc0d26e4df6e2942da6fadce4eaf47f3e1f3a6d445f36b83a8b5e71f97b2ecc756ae0dd12c23466fd754e97a56c + storybook: ^8.5.0-alpha.9 + checksum: 10c0/e2cbf1a61cf6f7afde60d8dbe79f4a4839f626e5b176f89ab634a04be49814799261ffbc811f09f6e709e0a465686b05a96bbc585bf464bb8999e04aad039b26 languageName: node linkType: hard -"@storybook/manager-api@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/manager-api@npm:8.4.4" +"@storybook/manager-api@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/manager-api@npm:8.5.0-alpha.9" peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10c0/00d83105b8cd451c7b420d96eefaa8918254a9c362d852f8e825dd02a861994b0e80fc190f6bfab95d742a947319178587bbccb64d9051ad8f1b36533e7cb4ed + checksum: 10c0/eedf3c815af83c0cf2ffca7f4839be8d6afba9ce151aa015d1dcd9c1ba6d4316294ad73793c7235443b0ee4b24f1b9136cab1e395f7be95f3a3f802bdfbdf797 languageName: node linkType: hard -"@storybook/preview-api@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/preview-api@npm:8.4.4" +"@storybook/preview-api@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/preview-api@npm:8.5.0-alpha.9" peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10c0/7adc0a4adde3a55cdd18fe645428a239c4d3802b92afdb17914719201893be8aec7cbd53203c9914c532ecd8970469df745f0097b677a04326e696470bc110cd + checksum: 10c0/e6346c01a06a7c4088885b5019255003c884935fb5aabb8e237070aed6e2e82ecc0f26f523b594e331890984460177fd1f1d23a370fb5cc3df411cb6de7cff29 languageName: node linkType: hard -"@storybook/react-dom-shim@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/react-dom-shim@npm:8.4.4" +"@storybook/react-dom-shim@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/react-dom-shim@npm:8.5.0-alpha.9" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.4 - checksum: 10c0/112467460ce65d23311bd9f559f5c6af1343287f98ab765695e4dda5d17f3d7acb696d96457087285b8781f20bd8e0ace26eb00624affdc2620b9699a9b28591 + storybook: ^8.5.0-alpha.9 + checksum: 10c0/059ede91debeb8d35a7d787d0c4792d69556fe37ca6ea04b751dea2cd6eb2add257f2ff47613669c719c292dd78b818e9a72e1563fa6c6a39d8545c7f982491d languageName: node linkType: hard -"@storybook/react-vite@npm:^8.4.0": - version: 8.4.4 - resolution: "@storybook/react-vite@npm:8.4.4" +"@storybook/react-vite@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/react-vite@npm:8.5.0-alpha.9" dependencies: "@joshwooding/vite-plugin-react-docgen-typescript": "npm:0.3.0" "@rollup/pluginutils": "npm:^5.0.2" - "@storybook/builder-vite": "npm:8.4.4" - "@storybook/react": "npm:8.4.4" + "@storybook/builder-vite": "npm:8.5.0-alpha.9" + "@storybook/react": "npm:8.5.0-alpha.9" find-up: "npm:^5.0.0" magic-string: "npm:^0.30.0" react-docgen: "npm:^7.0.0" @@ -2865,61 +2865,61 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.4 + storybook: ^8.5.0-alpha.9 vite: ^4.0.0 || ^5.0.0 - checksum: 10c0/2e3abd5b61fe05a2aa784a744d4700d1c5e2644cf5cc2b23b083a7227627a550c2cb8744f4571b693c6a24e783b931c1bd87441d89d960d6f5da7eef14c8a7e2 + checksum: 10c0/fd27513b4f0d477827aae36d2ecb2abb59a6f8482c5013ffc2a6b16cf39c4ea2fb42b0239c8f5b24d55554dc1cbf3dabf9df0760aadb1eee7ed497a5634c813d languageName: node linkType: hard -"@storybook/react@npm:8.4.4, @storybook/react@npm:^8.4.0": - version: 8.4.4 - resolution: "@storybook/react@npm:8.4.4" +"@storybook/react@npm:8.5.0-alpha.9, @storybook/react@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/react@npm:8.5.0-alpha.9" dependencies: - "@storybook/components": "npm:8.4.4" + "@storybook/components": "npm:8.5.0-alpha.9" "@storybook/global": "npm:^5.0.0" - "@storybook/manager-api": "npm:8.4.4" - "@storybook/preview-api": "npm:8.4.4" - "@storybook/react-dom-shim": "npm:8.4.4" - "@storybook/theming": "npm:8.4.4" + "@storybook/manager-api": "npm:8.5.0-alpha.9" + "@storybook/preview-api": "npm:8.5.0-alpha.9" + "@storybook/react-dom-shim": "npm:8.5.0-alpha.9" + "@storybook/theming": "npm:8.5.0-alpha.9" peerDependencies: - "@storybook/test": 8.4.4 + "@storybook/test": 8.5.0-alpha.9 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.4.4 + storybook: ^8.5.0-alpha.9 typescript: ">= 4.2.x" peerDependenciesMeta: "@storybook/test": optional: true typescript: optional: true - checksum: 10c0/aabbfeb09efca705ee23673b62673760bbfa60307ca0f36d562b3541cbd999f943d4c5537971b67bc84ad513f53e82a0be56b418d5777ea499c33c04c7ca935d + checksum: 10c0/23e566eee814b3f1c9371ba4cd5b6e9705fefcb08fed94f2db72ffd2dca61c0eca57df1455826ad727718a423c2a869bd0a07f82839ea7bea1ade9467c9b7088 languageName: node linkType: hard -"@storybook/test@npm:8.4.4, @storybook/test@npm:^8.4.0": - version: 8.4.4 - resolution: "@storybook/test@npm:8.4.4" +"@storybook/test@npm:8.5.0-alpha.9, @storybook/test@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/test@npm:8.5.0-alpha.9" dependencies: "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" - "@storybook/instrumenter": "npm:8.4.4" + "@storybook/instrumenter": "npm:8.5.0-alpha.9" "@testing-library/dom": "npm:10.4.0" "@testing-library/jest-dom": "npm:6.5.0" "@testing-library/user-event": "npm:14.5.2" "@vitest/expect": "npm:2.0.5" "@vitest/spy": "npm:2.0.5" peerDependencies: - storybook: ^8.4.4 - checksum: 10c0/9e02947a02664f9ba6d67b37515c8cc994c572a64274f2d50f68dfff212112aeafee6c870efbfb234ee09437bd4167616835254042a0eee89b914d9d6565d91e + storybook: ^8.5.0-alpha.9 + checksum: 10c0/564c1fa33f8277ca5b4fc44d5e401a364abcae655c2b0beba1575bf075bd0e7e9d440b58504f1afd3c9675fb496771420fbee22590cfde7b16acbd2f73d600b3 languageName: node linkType: hard -"@storybook/theming@npm:8.4.4": - version: 8.4.4 - resolution: "@storybook/theming@npm:8.4.4" +"@storybook/theming@npm:8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "@storybook/theming@npm:8.5.0-alpha.9" peerDependencies: storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 - checksum: 10c0/29b58bc6ef5ca7e2231f171ab360c12b3eeeac22747bed53cd61749f85cbe3f91c3cc6e3abf144f194043f624e79b3caa2ec6c77a0ea2584d7771b96061eb55e + checksum: 10c0/d5f60848b06d571af6c349017cde09d25b272398f2b2a5cdb734bd1d9f4a8fe934a462994c4a0f4363d4c970ec5968ad73de96bd1bb9b9de457d708da123bc37 languageName: node linkType: hard @@ -6146,13 +6146,13 @@ __metadata: dependencies: "@chromatic-com/storybook": "npm:^3.2.2" "@reduxjs/toolkit": "npm:^2.0.1" - "@storybook/addon-a11y": "npm:^8.4.0" - "@storybook/addon-essentials": "npm:^8.4.0" - "@storybook/blocks": "npm:^8.4.0" - "@storybook/experimental-addon-test": "npm:^8.4.4" - "@storybook/react": "npm:^8.4.0" - "@storybook/react-vite": "npm:^8.4.0" - "@storybook/test": "npm:^8.4.0" + "@storybook/addon-a11y": "npm:^8.5.0-alpha.9" + "@storybook/addon-essentials": "npm:^8.5.0-alpha.9" + "@storybook/blocks": "npm:^8.5.0-alpha.9" + "@storybook/experimental-addon-test": "npm:^8.5.0-alpha.9" + "@storybook/react": "npm:^8.5.0-alpha.9" + "@storybook/react-vite": "npm:^8.5.0-alpha.9" + "@storybook/test": "npm:^8.5.0-alpha.9" "@types/react": "npm:^18.2.66" "@types/react-dom": "npm:^18.2.22" "@vitejs/plugin-react": "npm:^4.2.1" @@ -6164,6 +6164,7 @@ __metadata: eslint-plugin-react-hooks: "npm:^4.6.0" eslint-plugin-react-refresh: "npm:^0.4.6" eslint-plugin-storybook: "npm:^0.8.0" + mockdate: "npm:^3.0.5" msw: "npm:^2.3.0" msw-storybook-addon: "npm:^2.0.3" playwright: "npm:^1.49.0" @@ -6171,7 +6172,7 @@ __metadata: react: "npm:^18.2.0" react-dom: "npm:^18.2.0" react-redux: "npm:^9.0.4" - storybook: "npm:^8.4.0" + storybook: "npm:^8.5.0-alpha.9" ts-migrate: "npm:^0.1.35" typescript: "npm:^5.6.3" vite: "npm:^5.2.0" @@ -7373,6 +7374,13 @@ __metadata: languageName: node linkType: hard +"mockdate@npm:^3.0.5": + version: 3.0.5 + resolution: "mockdate@npm:3.0.5" + checksum: 10c0/36ab00c4b94d3e3cc8b9ecec27730dcf396d47d2ba046ad96635e7f9bc4bba0fff6125dedaba7313b1cadc0cefc0eb3f3ac0d5c3655707afd3b82fa4550aae92 + languageName: node + linkType: hard + "mrmime@npm:^2.0.0": version: 2.0.0 resolution: "mrmime@npm:2.0.0" @@ -9102,11 +9110,11 @@ __metadata: languageName: node linkType: hard -"storybook@npm:^8.4.0": - version: 8.4.4 - resolution: "storybook@npm:8.4.4" +"storybook@npm:^8.5.0-alpha.9": + version: 8.5.0-alpha.9 + resolution: "storybook@npm:8.5.0-alpha.9" dependencies: - "@storybook/core": "npm:8.4.4" + "@storybook/core": "npm:8.5.0-alpha.9" peerDependencies: prettier: ^2 || ^3 peerDependenciesMeta: @@ -9116,7 +9124,7 @@ __metadata: getstorybook: ./bin/index.cjs sb: ./bin/index.cjs storybook: ./bin/index.cjs - checksum: 10c0/cf680b8451fc4941812af91844b332fec83aa71739e1ce528bb6de623d89b119b7db6c4a3f6b4e600604d39e3c4e04f0cc6d500815c473da7ee676208fa3c782 + checksum: 10c0/13f93f27d4461207031f031bf105e5589157a435a21907720cefcbd9b5303b74598a9592a0eb092cebc588fd1b4134e92fd65c5ae39714278981d98b2cb71513 languageName: node linkType: hard