From 2057176f6304d2ba0638551c0bc55843a52eefce Mon Sep 17 00:00:00 2001 From: SorayaFerreira Date: Mon, 24 Nov 2025 18:11:19 -0400 Subject: [PATCH 1/7] ref: replace forced window.location.reload() with SPA navigation via publicAppRouterInstance.replace, falling back to window.location.replace --- packages/core/src/src.und/id.txt | 1 + packages/core/src/src.und/settings.xml | 222 +++++++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 packages/core/src/src.und/id.txt create mode 100644 packages/core/src/src.und/settings.xml diff --git a/packages/core/src/src.und/id.txt b/packages/core/src/src.und/id.txt new file mode 100644 index 0000000..259d958 --- /dev/null +++ b/packages/core/src/src.und/id.txt @@ -0,0 +1 @@ +{e4051284-b6e5-43f9-a499-cdaa25138434} \ No newline at end of file diff --git a/packages/core/src/src.und/settings.xml b/packages/core/src/src.und/settings.xml new file mode 100644 index 0000000..1d6d072 --- /dev/null +++ b/packages/core/src/src.und/settings.xml @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f75ed78fbf973f9fef01daa805f638cb19362902 Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 24 Nov 2025 18:58:03 -0400 Subject: [PATCH 2/7] refactor: Fixing too many props issue --- .../sb-react/src/dnd/drag-line.stories.tsx | 119 ++++++++++-------- 1 file changed, 65 insertions(+), 54 deletions(-) diff --git a/packages/sb-react/src/dnd/drag-line.stories.tsx b/packages/sb-react/src/dnd/drag-line.stories.tsx index 83868fa..e63488b 100644 --- a/packages/sb-react/src/dnd/drag-line.stories.tsx +++ b/packages/sb-react/src/dnd/drag-line.stories.tsx @@ -7,6 +7,7 @@ import { keyboardDragAndDropFeature, selectionFeature, syncDataLoaderFeature, + TreeItemInstance, } from "@headless-tree/core"; import { AssistiveTreeDescription, useTree } from "@headless-tree/react"; import cn from "classnames"; @@ -18,79 +19,89 @@ const meta = { export default meta; -// story-start +const STATIC_FEATURES = [ + syncDataLoaderFeature, + selectionFeature, + hotkeysCoreFeature, + dragAndDropFeature, + keyboardDragAndDropFeature, +]; + +const mockDataLoader = { + getItem: (itemId: string) => itemId, + getChildren: (itemId: string) => + Array.from({ length: 6 }, (_, i) => `${itemId}-${i + 1}`), +}; + +const handleDrop = (items: TreeItemInstance[], target: any) => { + alert( + `Dropped ${items.map((item) => item.getId())} on ${target.item.getId()}, ${JSON.stringify(target)}` + ); +}; + +interface TreeItemRowProps { + item: TreeItemInstance; +} + +const TreeItemRow = React.memo(({ item }: TreeItemRowProps) => { + return ( + + ); +}); + +TreeItemRow.displayName = "TreeItemRow"; + export const DragLine = () => { - const [state, setState] = useState>>({ + const [state, setState] = useState>>({ expandedItems: ["root-1", "root-1-2"], }); + const tree = useTree({ state, setState, rootItemId: "root", + dataLoader: mockDataLoader, + onDrop: handleDrop, getItemName: (item) => item.getItemData(), isItemFolder: () => true, canReorder: true, - onDrop: (items, target) => { - alert( - `Dropped ${items.map((item) => - item.getId(), - )} on ${target.item.getId()}, ${JSON.stringify(target)}`, - ); - }, indent: 20, - dataLoader: { - getItem: (itemId) => itemId, - getChildren: (itemId) => [ - `${itemId}-1`, - `${itemId}-2`, - `${itemId}-3`, - `${itemId}-4`, - `${itemId}-5`, - `${itemId}-6`, - ], - }, - features: [ - syncDataLoaderFeature, - selectionFeature, - hotkeysCoreFeature, - dragAndDropFeature, - keyboardDragAndDropFeature, - ], + features: STATIC_FEATURES, }); - const dragLine = tree.getDragLineData(); + const dragLineStyle = tree.getDragLineStyle(); + const dragLineData = tree.getDragLineData(); + const items = tree.getItems(); return (
- {tree.getItems().map((item) => ( - + + {items.map((item) => ( + ))} -
+ + {dragLineData && ( +
+ )} +
-        {JSON.stringify(
-          {
-            dragLine,
-          },
-          null,
-          2,
-        )}
+        {JSON.stringify({ dragLine: dragLineData }, null, 2)}
       
); From 852059122844f717ea4e977310048d4880389b7e Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 24 Nov 2025 19:00:27 -0400 Subject: [PATCH 3/7] refactor: refactoring file to avoid too many props issue --- .../sb-react/src/dnd/drag-preview.stories.tsx | 168 +++++++++++------- 1 file changed, 99 insertions(+), 69 deletions(-) diff --git a/packages/sb-react/src/dnd/drag-preview.stories.tsx b/packages/sb-react/src/dnd/drag-preview.stories.tsx index ef49252..b8504fc 100644 --- a/packages/sb-react/src/dnd/drag-preview.stories.tsx +++ b/packages/sb-react/src/dnd/drag-preview.stories.tsx @@ -7,6 +7,7 @@ import { keyboardDragAndDropFeature, selectionFeature, syncDataLoaderFeature, + TreeItemInstance, } from "@headless-tree/core"; import { AssistiveTreeDescription, useTree } from "@headless-tree/react"; import cn from "classnames"; @@ -18,12 +19,99 @@ const meta = { export default meta; -// story-start +const STATIC_FEATURES = [ + syncDataLoaderFeature, + selectionFeature, + hotkeysCoreFeature, + dragAndDropFeature, + keyboardDragAndDropFeature, +]; + +const mockDataLoader = { + getItem: (itemId: string) => itemId, + getChildren: (itemId: string) => [ + `${itemId}-1`, + `${itemId}-2`, + `${itemId}-3`, + `${itemId}-4`, + ], +}; + +const handleDrop = (items: TreeItemInstance[], target: any) => { + alert( + `Dropped ${items.map((item) => item.getId())} on ${target.item.getId()}, ${JSON.stringify(target)}` + ); +}; + +const handleSetDragImage = () => ({ + imgElement: document.getElementById("dragpreview")!, + xOffset: -20, +}); + +interface CustomDragPreviewProps { + draggedItems?: TreeItemInstance[]; +} + +const CustomDragPreview = React.memo(({ draggedItems }: CustomDragPreviewProps) => { + return ( +
+ Dragging{" "} + {draggedItems + ?.slice(0, 3) + .map((item) => item.getItemName()) + .join(", ")} + {(draggedItems?.length ?? 0) > 3 && + ` and ${(draggedItems?.length ?? 0) - 3} more`} +
+ ); +}); + +CustomDragPreview.displayName = "CustomDragPreview"; + +interface TreeItemRowProps { + item: TreeItemInstance; +} + +const TreeItemRow = React.memo(({ item }: TreeItemRowProps) => { + return ( + + ); +}); + +TreeItemRow.displayName = "TreeItemRow"; + export const DragPreview = () => { - const [state, setState] = useState>>({ + const [state, setState] = useState>>({ expandedItems: ["root-1", "root-1-2"], selectedItems: ["root-1-2-1", "root-1-2-2", "root-1-2-3", "root-1-2-4"], }); + const tree = useTree({ state, setState, @@ -31,34 +119,11 @@ export const DragPreview = () => { getItemName: (item) => item.getItemData(), isItemFolder: () => true, canReorder: true, - onDrop: (items, target) => { - alert( - `Dropped ${items.map((item) => - item.getId(), - )} on ${target.item.getId()}, ${JSON.stringify(target)}`, - ); - }, - setDragImage: () => ({ - imgElement: document.getElementById("dragpreview")!, - xOffset: -20, - }), + onDrop: handleDrop, + setDragImage: handleSetDragImage, indent: 20, - dataLoader: { - getItem: (itemId) => itemId, - getChildren: (itemId) => [ - `${itemId}-1`, - `${itemId}-2`, - `${itemId}-3`, - `${itemId}-4`, - ], - }, - features: [ - syncDataLoaderFeature, - selectionFeature, - hotkeysCoreFeature, - dragAndDropFeature, - keyboardDragAndDropFeature, - ], + dataLoader: mockDataLoader, + features: STATIC_FEATURES, }); const draggedItems = tree.getState().dnd?.draggedItems; @@ -71,49 +136,14 @@ export const DragPreview = () => {

+ {tree.getItems().map((item) => ( - + ))} +
-
- Dragging{" "} - {draggedItems - ?.slice(0, 3) - .map((item) => item.getItemName()) - .join(", ")} - {(draggedItems?.length ?? 0) > 3 && - ` and ${(draggedItems?.length ?? 0) - 3} more`} -
+ +
); From dce6832f005181e318fca68692b76263c30538e1 Mon Sep 17 00:00:00 2001 From: Mylenna Farias <134342470+mylennapf@users.noreply.github.com> Date: Mon, 24 Nov 2025 23:01:05 +0000 Subject: [PATCH 4/7] refactor: resolve DOM Directly DOM manipulations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adiciona exemplo simulado de refatoração demonstrando code smell de manipulação de DOM - Mostra uso inadequado de innerHTML e getElementById - Arquivo criado para fins educacionais --- examples/comprehensive/src/simulated-refactor.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 examples/comprehensive/src/simulated-refactor.tsx diff --git a/examples/comprehensive/src/simulated-refactor.tsx b/examples/comprehensive/src/simulated-refactor.tsx new file mode 100644 index 0000000..9342cb3 --- /dev/null +++ b/examples/comprehensive/src/simulated-refactor.tsx @@ -0,0 +1,12 @@ +// Simulated DOM manipulation code smell +export function BadComponent() { + // DOM manipulation code smell + const updateElement = () => { + const element = document.getElementById('my-div'); + if (element) { + element.innerHTML = 'New content'; + } + }; + + return ; +} From 8822d48b52a29f1035dc70136ac17dfdaa43f3ab Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 24 Nov 2025 19:01:45 -0400 Subject: [PATCH 5/7] refactor: refactoring script to avoid too many props issue --- .../sb-react/src/dnd/kitchensink.stories.tsx | 159 +++++++++++------- 1 file changed, 95 insertions(+), 64 deletions(-) diff --git a/packages/sb-react/src/dnd/kitchensink.stories.tsx b/packages/sb-react/src/dnd/kitchensink.stories.tsx index 0fd1083..c547d51 100644 --- a/packages/sb-react/src/dnd/kitchensink.stories.tsx +++ b/packages/sb-react/src/dnd/kitchensink.stories.tsx @@ -6,6 +6,7 @@ import { keyboardDragAndDropFeature, selectionFeature, syncDataLoaderFeature, + TreeItemInstance, } from "@headless-tree/core"; import { AssistiveTreeDescription, useTree } from "@headless-tree/react"; import { action } from "storybook/actions"; @@ -36,13 +37,91 @@ const meta = { export default meta; -// story-start +const STATIC_FEATURES = [ + syncDataLoaderFeature, + selectionFeature, + hotkeysCoreFeature, + dragAndDropFeature, + keyboardDragAndDropFeature, +]; + +const mockDataLoader = { + getItem: (itemId: string) => itemId, + getChildren: (itemId: string) => [ + `${itemId}-1`, + `${itemId}-2`, + `${itemId}-3`, + `${itemId}-4`, + `${itemId}-5`, + `${itemId}-6`, + ], +}; + +const createForeignDragObject = (items: TreeItemInstance[]) => ({ + format: "text/plain", + data: `custom foreign drag object: ${items.map((item) => item.getId()).join(",")}`, +}); + +const handleDropAction = action("onDrop"); +const handleDropForeignDragObjectAction = action("onDropForeignDragObject"); +const handleExternalDropAction = action("onDropExternally"); + +interface TreeItemRowProps { + item: TreeItemInstance; +} + +const TreeItemRow = React.memo(({ item }: TreeItemRowProps) => { + return ( + + ); +}); + +TreeItemRow.displayName = "TreeItemRow"; + +const ExternalDropTarget = () => ( +
handleExternalDropAction(e.dataTransfer.getData("text/plain"))} + onDragOver={(e) => e.preventDefault()} + > + Drop items here! +
+); + +const ExternalDraggableSource = () => ( +
{ + e.dataTransfer.setData("text/plain", "hello world"); + }} + > + Or drag me into the tree! +
+); + export const KitchenSink = ({ canReorder, canDropForeignDragObject, reorderAreaPercentage, }: PropsOfArgtype) => { const [state, setState] = useState({}); + const tree = useTree({ state, setState, @@ -50,80 +129,32 @@ export const KitchenSink = ({ getItemName: (item) => item.getItemData(), isItemFolder: () => true, canReorder, - onDrop: action("onDrop"), - onDropForeignDragObject: action("onDropForeignDragObject"), - createForeignDragObject: (items) => ({ - format: "text/plain", - data: `custom foreign drag object: ${items - .map((item) => item.getId()) - .join(",")}`, - }), - canDropForeignDragObject: () => canDropForeignDragObject, reorderAreaPercentage, + canDropForeignDragObject: () => canDropForeignDragObject ?? false, + createForeignDragObject, + onDrop: handleDropAction, + onDropForeignDragObject: handleDropForeignDragObjectAction, indent: 20, - dataLoader: { - getItem: (itemId) => itemId, - getChildren: (itemId) => [ - `${itemId}-1`, - `${itemId}-2`, - `${itemId}-3`, - `${itemId}-4`, - `${itemId}-5`, - `${itemId}-6`, - ], - }, - features: [ - syncDataLoaderFeature, - selectionFeature, - hotkeysCoreFeature, - dragAndDropFeature, - keyboardDragAndDropFeature, - ], + dataLoader: mockDataLoader, + features: STATIC_FEATURES, }); + const dragLineStyle = tree.getDragLineStyle(); + return ( <>
+ {tree.getItems().map((item) => ( - + ))} -
-
-
- action("onDropExternally")(e.dataTransfer.getData("text/plain")) - } - onDragOver={(e) => e.preventDefault()} - > - Drop items here! -
-
{ - e.dataTransfer.setData("text/plain", "hello world"); - }} - > - Or drag me into the tree! + +
+ + + ); }; From 73f75396ace4662cf8710c263158682dd24c5e52 Mon Sep 17 00:00:00 2001 From: Sophya Ribeiro Date: Tue, 25 Nov 2025 14:11:40 -0400 Subject: [PATCH 6/7] remove code smell --- ...checked-state-as-radio-buttons.stories.tsx | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/packages/sb-react/src/checkboxes/checked-state-as-radio-buttons.stories.tsx b/packages/sb-react/src/checkboxes/checked-state-as-radio-buttons.stories.tsx index d37b5b3..76cc3d1 100644 --- a/packages/sb-react/src/checkboxes/checked-state-as-radio-buttons.stories.tsx +++ b/packages/sb-react/src/checkboxes/checked-state-as-radio-buttons.stories.tsx @@ -1,5 +1,5 @@ import type { Meta } from "@storybook/react"; -import React from "react"; +import React, { JSX } from "react"; import { CheckedState, type FeatureImplementation, @@ -54,6 +54,36 @@ const checkboxOverride: FeatureImplementation = { }, }; + +interface TreeViewProps { + tree: TreeInstance; +} + +const TreeView: React.FC = ({ tree }) => ( +
+ {tree.getItems().map((item: TreeInstance["getItems"][number]) => ( +
+ + +
+ ))} +
+); + export const CheckedStateAsRadioButtons = () => { const tree = useTree({ rootItemId: "root", @@ -61,8 +91,8 @@ export const CheckedStateAsRadioButtons = () => { expandedItems: ["fruit", "berries", "red"], checkedItems: ["fruit", "banana", "berries", "strawberry"], }, - getItemName: (item) => item.getItemData().name, - isItemFolder: (item) => !!item.getItemData().children, + getItemName: (item: TreeInstance["getItems"][number]) => item.getItemData().name, + isItemFolder: (item: TreeInstance["getItems"][number]) => !!item.getItemData().children, dataLoader: syncDataLoader, canCheckFolders: true, propagateCheckedState: false, @@ -82,31 +112,8 @@ export const CheckedStateAsRadioButtons = () => { In this sample, a custom "toggleCheckedState" implementation is used to enforce that for each folder, only one child can be checked.

- -
- {tree.getItems().map((item) => ( -
- - -
- ))} -
- +
{JSON.stringify(tree.getState().checkedItems)}
); -}; +} From b112c29d3a3d1ec8ff944cc2e7434fe3706c985b Mon Sep 17 00:00:00 2001 From: Soraya Ferreira <113856615+SorayaFerreira@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:24:42 -0400 Subject: [PATCH 7/7] Delete .github/workflows directory --- .github/workflows/docs.yml | 38 ------------------- .github/workflows/publish.yml | 54 --------------------------- .github/workflows/snapshot.yml | 67 ---------------------------------- .github/workflows/verify.yml | 24 ------------ 4 files changed, 183 deletions(-) delete mode 100644 .github/workflows/docs.yml delete mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/snapshot.yml delete mode 100644 .github/workflows/verify.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 0464c39..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Docs -on: - push: - branches: - - main - workflow_dispatch: - -permissions: - contents: read - pages: write - deployments: write - id-token: write - -jobs: - docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v3 - with: - path: | - **/node_modules - ./.yarn/cache - key: ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }} - - uses: volta-cli/action@v4.1.1 - - run: yarn - - run: yarn lint:test - - run: yarn build:core - - run: yarn build:sb - - run: yarn llmtxt - - run: yarn build:web - - run: | - mkdir -p ./packages/docs/build/storybook - cp -r ./packages/sb-react/storybook-static ./packages/docs/build/storybook/react - - uses: actions/upload-pages-artifact@v3 - with: - path: ./packages/docs/build - - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 06e47ac..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Publish -on: - push: - branches: - - main - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - id-token: write - -jobs: - publish: - runs-on: ubuntu-latest - name: Publish - steps: - - uses: actions/checkout@v3 - with: - persist-credentials: false - fetch-depth: 0 - - - uses: actions/cache@v3 - with: - path: | - **/node_modules - ./.yarn/cache - key: ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }} - - uses: volta-cli/action@v4.1.1 - with: - registry-url: "https://registry.npmjs.org" - - - run: yarn - - run: yarn lint:test - - run: yarn test - - run: yarn build:core - - run: yarn build:web - - name: Copy readme.md to packages - run: | - for dir in $(find packages -type d -maxdepth 1); do - cp readme.md $dir/readme.md - done - - - name: Create Release Pull Request or Publish to npm - id: changesets - uses: changesets/action@v1 - with: - version: npx zx ./scripts/version.mjs - publish: yarn changeset publish - commit: "chore: release [nosnapshot]" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml deleted file mode 100644 index 08d85e4..0000000 --- a/.github/workflows/snapshot.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Snapshot Release -on: - push: - branches: - - main - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - id-token: write - -jobs: - snapshot: - if: ${{ !contains(github.event.commits[0].message, 'nosnapshot') }} - runs-on: ubuntu-latest - name: Publish - steps: - - uses: actions/checkout@v3 - with: - persist-credentials: false - fetch-depth: 0 - - - name: check skip tag - run: | - if git log -1 --pretty=%B | grep -qE 'chore: release|nosnapshot'; then - echo "Skipping snapshot release due to commit message." - exit 0 - fi - - - uses: actions/cache@v3 - with: - path: | - **/node_modules - ./.yarn/cache - key: ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }} - - uses: volta-cli/action@v4.1.1 - with: - registry-url: "https://registry.npmjs.org" - - - run: yarn - - run: yarn lint:test - - run: yarn test - - run: yarn build:core - - run: yarn build:web - - - name: Copy readme.md to packages - run: | - for dir in $(find packages -type d -maxdepth 1); do - cp readme.md $dir/readme.md - done - - - name: Snapshot Versioning - run: yarn changeset version --snapshot - - - name: Snapshot Release - run: | - if git diff --quiet; then - echo "No changes detected" - exit 0 - else - yarn changeset publish --no-git-tag --tag snapshot - fi - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml deleted file mode 100644 index 410b9df..0000000 --- a/.github/workflows/verify.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Verify -on: - push: - pull_request: - workflow_dispatch: - -jobs: - verify: - runs-on: ubuntu-latest - name: Verify - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - **/node_modules - ./.yarn/cache - key: ${{ runner.os }}-deps-${{ hashFiles('**/yarn.lock') }} - - uses: volta-cli/action@v4.1.1 - - run: yarn - - run: yarn lint:test - - run: yarn test - - run: yarn build:core - - run: yarn build:web