@vtex/faststore-plugin-buyer-portal 1.3.13 → 1.3.14

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
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.14] - 2025-11-03
11
+
12
+ ### Fixed
13
+
14
+ - Click outside on Dropdown are now properly closing the dropdown on Budget allocation selection
15
+
10
16
  ## [1.3.13] - 2025-11-03
11
17
 
12
18
  - Setup OTLP to send log events on error boundary
@@ -175,7 +181,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
175
181
  - Add CHANGELOG file
176
182
  - Add README file
177
183
 
178
- [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.13...HEAD
184
+ [unreleased]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.14...HEAD
179
185
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.2.2...1.2.3
180
186
  [1.2.3]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.3
181
187
  [1.2.4]: https://github.com/vtex/faststore-plugin-buyer-portal/releases/tag/1.2.4
@@ -190,6 +196,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
190
196
 
191
197
  # <<<<<<< HEAD
192
198
 
199
+ [1.3.14]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.13...v1.3.14
193
200
  [1.3.13]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.12...v1.3.13
194
201
  [1.3.12]: https://github.com/vtex/faststore-plugin-buyer-portal/compare/v1.3.11...v1.3.12
195
202
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtex/faststore-plugin-buyer-portal",
3
- "version": "1.3.13",
3
+ "version": "1.3.14",
4
4
  "description": "A plugin for faststore with buyer portal",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,4 +1,4 @@
1
- import { useEffect, useRef } from "react";
1
+ import { useEffect, useRef, useState } from "react";
2
2
 
3
3
  import {
4
4
  Checkbox,
@@ -16,6 +16,7 @@ import { getTableColumns } from "../../../shared/components/Table/utils/tableCol
16
16
  import { useBuyerPortal } from "../../../shared/hooks";
17
17
  import { IPagination } from "../../../shared/types";
18
18
  import { Dictionary } from "../../../shared/utils";
19
+ import { useClickOutside } from "../../hooks";
19
20
 
20
21
  import type { BudgetAllocationsSelectionDataType } from "../../types";
21
22
 
@@ -48,12 +49,19 @@ export const BudgetAllocationsSelection = ({
48
49
  onSortChange,
49
50
  setFilter,
50
51
  }: BudgetAllocationsSelectionProps) => {
52
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
53
+
51
54
  const { currentContract: contract } = useBuyerPortal();
55
+
52
56
  const options = Object.keys(sortingOptions);
53
57
 
54
58
  const svgProps = { width: 10, height: 10 };
55
59
  const selectAllRef = useRef<HTMLInputElement>(null);
56
60
 
61
+ const dropdownRef = useClickOutside<HTMLDivElement>(() => {
62
+ setIsDropdownOpen(false);
63
+ });
64
+
57
65
  const updateCurrentSort = (newSort: string) => {
58
66
  onSortChange(newSort);
59
67
  };
@@ -155,8 +163,11 @@ export const BudgetAllocationsSelection = ({
155
163
  return (
156
164
  <div data-fs-bp-budget-allocation-table>
157
165
  <div data-fs-bp-budget-allocation-wrapper>
158
- <Dropdown>
159
- <DropdownButton data-fs-bp-budget-allocation-select-current>
166
+ <Dropdown isOpen={isDropdownOpen}>
167
+ <DropdownButton
168
+ data-fs-bp-budget-allocation-select-current
169
+ onClick={() => setIsDropdownOpen((prev) => !prev)}
170
+ >
160
171
  {currentSort}
161
172
  <Icon
162
173
  name="CaretRight"
@@ -166,21 +177,27 @@ export const BudgetAllocationsSelection = ({
166
177
  />
167
178
  </DropdownButton>
168
179
  <DropdownMenu align="left">
169
- {options.map((option) => (
170
- <DropdownItem
171
- key={option}
172
- onClick={() => updateCurrentSort(option)}
173
- data-fs-bp-budget-allocation-select-item
174
- data-fs-bp-budget-allocation-selected={currentSort === option}
175
- >
176
- <div data-fs-bp-budget-allocation-selected-wrapper>
177
- <span>{option}</span>
178
- {currentSort === option && (
179
- <Icon name="Check" width={16} height={16} />
180
- )}
181
- </div>
182
- </DropdownItem>
183
- ))}
180
+ <div ref={dropdownRef}>
181
+ {options.map((option) => (
182
+ <DropdownItem
183
+ key={option}
184
+ onClick={(e) => {
185
+ e.stopPropagation();
186
+ updateCurrentSort(option);
187
+ setIsDropdownOpen(false);
188
+ }}
189
+ data-fs-bp-budget-allocation-select-item
190
+ data-fs-bp-budget-allocation-selected={currentSort === option}
191
+ >
192
+ <div data-fs-bp-budget-allocation-selected-wrapper>
193
+ <span>{option}</span>
194
+ {currentSort === option && (
195
+ <Icon name="Check" width={16} height={16} />
196
+ )}
197
+ </div>
198
+ </DropdownItem>
199
+ ))}
200
+ </div>
184
201
  </DropdownMenu>
185
202
  </Dropdown>
186
203
 
@@ -137,5 +137,6 @@
137
137
  align-items: center;
138
138
  justify-content: space-between;
139
139
  width: 100%;
140
+ gap: var(--fs-spacing-2);
140
141
  }
141
142
  }
@@ -8,3 +8,4 @@ export { useListBudgets } from "./useListBudgets";
8
8
  export { useUpdateBudget } from "./useUpdateBudget";
9
9
  export { useListUsers } from "./useListUsers";
10
10
  export { useListAddresses } from "./useListAddressess";
11
+ export { useClickOutside } from "./useClickOutside";
@@ -0,0 +1,26 @@
1
+ import { RefObject, useEffect, useRef } from "react";
2
+
3
+ export function useClickOutside<T extends HTMLElement>(
4
+ callback: () => void
5
+ ): RefObject<T> {
6
+ const ref = useRef<T>(null);
7
+ const callbackRef = useRef(callback);
8
+
9
+ // Update callbackRef to always hold the latest callback
10
+ useEffect(() => {
11
+ callbackRef.current = callback;
12
+ }, [callback]);
13
+
14
+ useEffect(() => {
15
+ const handleClick = (event: Event) => {
16
+ if (ref.current && !ref.current.contains(event.target as Node)) {
17
+ callbackRef.current();
18
+ }
19
+ };
20
+
21
+ document.addEventListener("click", handleClick, true);
22
+ return () => document.removeEventListener("click", handleClick, true);
23
+ }, []);
24
+
25
+ return ref;
26
+ }
@@ -62,7 +62,7 @@ export const CustomFieldsLayout = ({
62
62
  const lastSearchValue = useRef(filter.search);
63
63
  const contractId = contract?.id ?? "";
64
64
  const unitId = orgUnit?.id ?? "";
65
- const cookie = clientContext.cookie;
65
+ const cookie = clientContext?.cookie ?? "";
66
66
  const debouncedSearchValue = useDebounce(filter.search, 500);
67
67
 
68
68
  useEffect(() => {
@@ -26,13 +26,13 @@ export type UsePageItemsProps<T> = {
26
26
  };
27
27
 
28
28
  export const usePageItems = <T>({
29
- initialItems,
29
+ initialItems = [],
30
30
  search,
31
31
  page = 1,
32
32
  searchParam = SEARCH_PARAMS.DEFAULT,
33
33
  pageParam = PAGE_PARAMS.DEFAULT,
34
34
  }: UsePageItemsProps<T>) => {
35
- const [items, setItems] = useState<T[]>(initialItems ?? []);
35
+ const [items, setItems] = useState<T[]>(initialItems);
36
36
  const [total, setTotal] = useState<number>(initialItems.length);
37
37
  const [searchTerm, setSearchTerm] = useState(search ?? "");
38
38
  const [isLoading, setIsLoading] = useState(false);
@@ -13,4 +13,4 @@ export const LOCAL_STORAGE_LOCATION_EDIT_KEY = "bp_hide_edit_location_confirm";
13
13
  export const LOCAL_STORAGE_RECIPIENT_EDIT_KEY =
14
14
  "bp_hide_edit_recipient_confirm";
15
15
 
16
- export const CURRENT_VERSION = "1.3.13";
16
+ export const CURRENT_VERSION = "1.3.14";