@faststore/core 4.9.0-dev.3 → 4.9.0-dev.5

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.
Files changed (39) hide show
  1. package/.turbo/turbo-generate.log +3 -3
  2. package/.turbo/turbo-test.log +132 -138
  3. package/CHANGELOG.md +12 -0
  4. package/cms/faststore/my-account/components/cms_component__accountlistquotes.jsonc +240 -0
  5. package/cms/faststore/my-account/components/cms_component__accountnavigation.jsonc +76 -0
  6. package/cms/faststore/my-account/pages/cms_content_type__myaccountquotes.jsonc +18 -0
  7. package/package.json +6 -6
  8. package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +129 -26
  9. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +7 -1
  10. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +42 -11
  11. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +45 -15
  12. package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +11 -3
  13. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +23 -0
  14. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +35 -11
  15. package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +27 -10
  16. package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
  17. package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +16 -6
  18. package/src/components/account/quotes/MyAccountListQuotes/quotesLabels.ts +163 -0
  19. package/src/components/cms/account/Components.ts +2 -0
  20. package/src/components/sections/Account/AccountListQuotes.tsx +141 -0
  21. package/src/pages/pvt/account/quotes/index.tsx +59 -40
  22. package/src/pages/{s.tsx → s/index.tsx} +11 -0
  23. package/src/sdk/account/accountPageContext.ts +17 -0
  24. package/src/sdk/account/getMyAccountRoutes.ts +17 -0
  25. package/src/sdk/account/resolveNonBlankLabels.ts +36 -0
  26. package/src/server/cms/myAccountDefaultSections.ts +2 -0
  27. package/src/utils/quoteStatus.ts +81 -0
  28. package/test/components/account/ContractSwitcher.browser.test.tsx +163 -0
  29. package/test/components/account/OrganizationDrawer.browser.test.tsx +4 -0
  30. package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +44 -1
  31. package/test/components/quotes/MyAccountListQuotes.test.tsx +36 -2
  32. package/test/components/quotes/MyAccountListQuotesTable.test.tsx +68 -0
  33. package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +25 -0
  34. package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +52 -11
  35. package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +32 -0
  36. package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +100 -0
  37. package/test/components/quotes/quoteFilters.test.ts +34 -0
  38. package/test/components/sections/Account/AccountListQuotes.browser.test.tsx +122 -0
  39. package/test/server/cms/myAccountDefaultSections.test.ts +21 -0
@@ -82,4 +82,104 @@ describe('MyAccountQuotesStatusSelector', () => {
82
82
 
83
83
  expect(screen.getByText('NotAStatus')).toBeTruthy()
84
84
  })
85
+
86
+ it('renders the default "Status" label when no override is given', () => {
87
+ render(<MyAccountQuotesStatusSelector value={[]} onChange={vi.fn()} />)
88
+
89
+ expect(screen.getByText('Status')).toBeTruthy()
90
+ })
91
+
92
+ it('honors a CMS-provided field label', () => {
93
+ render(
94
+ <MyAccountQuotesStatusSelector
95
+ value={[]}
96
+ onChange={vi.fn()}
97
+ statusLabel="Estado"
98
+ />
99
+ )
100
+
101
+ expect(screen.getByText('Estado')).toBeTruthy()
102
+ expect(screen.queryByText('Status')).toBeNull()
103
+ })
104
+
105
+ it('honors CMS-provided status option labels for chips and the dropdown', () => {
106
+ const { container } = render(
107
+ <MyAccountQuotesStatusSelector
108
+ value={['Draft']}
109
+ onChange={vi.fn()}
110
+ statusCmsLabels={{ draftStatus: 'Rascunho' }}
111
+ />
112
+ )
113
+
114
+ expect(screen.getByText('Rascunho')).toBeTruthy()
115
+
116
+ fireEvent.click(
117
+ container.querySelector('[aria-controls="status-listbox"]')!
118
+ )
119
+
120
+ expect(screen.getByLabelText('Rascunho')).toBeTruthy()
121
+ })
122
+
123
+ it('honors a CMS-provided remove-chip aria-label template', () => {
124
+ render(
125
+ <MyAccountQuotesStatusSelector
126
+ value={['Draft']}
127
+ onChange={vi.fn()}
128
+ removeStatusAriaLabel="Remover {status}"
129
+ />
130
+ )
131
+
132
+ expect(screen.getByLabelText('Remover Draft')).toBeTruthy()
133
+ expect(screen.queryByLabelText('Remove Draft')).toBeNull()
134
+ })
135
+
136
+ it('substitutes the localized status label into the remove-chip aria-label', () => {
137
+ render(
138
+ <MyAccountQuotesStatusSelector
139
+ value={['Draft']}
140
+ onChange={vi.fn()}
141
+ removeStatusAriaLabel="Remover {status}"
142
+ statusCmsLabels={{ draftStatus: 'Rascunho' }}
143
+ />
144
+ )
145
+
146
+ expect(screen.getByLabelText('Remover Rascunho')).toBeTruthy()
147
+ })
148
+
149
+ it('inserts a status label that contains $ verbatim into the remove aria-label', () => {
150
+ render(
151
+ <MyAccountQuotesStatusSelector
152
+ value={['Draft']}
153
+ onChange={vi.fn()}
154
+ removeStatusAriaLabel="Remove {status}"
155
+ statusCmsLabels={{ draftStatus: 'Ready $& soon' }}
156
+ />
157
+ )
158
+
159
+ expect(screen.getByLabelText('Remove Ready $& soon')).toBeTruthy()
160
+ })
161
+
162
+ it('keeps a remove-chip template that has no status token', () => {
163
+ render(
164
+ <MyAccountQuotesStatusSelector
165
+ value={['Draft']}
166
+ onChange={vi.fn()}
167
+ removeStatusAriaLabel="Remove selected status"
168
+ />
169
+ )
170
+
171
+ expect(screen.getByLabelText('Remove selected status')).toBeTruthy()
172
+ })
173
+
174
+ it('replaces only the first status token and keeps the rest of the template', () => {
175
+ render(
176
+ <MyAccountQuotesStatusSelector
177
+ value={['Draft']}
178
+ onChange={vi.fn()}
179
+ removeStatusAriaLabel="Remove {status} ({status})"
180
+ />
181
+ )
182
+
183
+ expect(screen.getByLabelText('Remove Draft ({status})')).toBeTruthy()
184
+ })
85
185
  })
@@ -103,6 +103,40 @@ describe('getAllFacets', () => {
103
103
  expect(createdAt.from).toBe('2024-01-01')
104
104
  expect(createdAt.to).toBe('2024-12-31')
105
105
  })
106
+
107
+ it('uses default English labels when no CMS labels are provided', () => {
108
+ const facets = getAllFacets(emptyFilters)
109
+ expect(facets[0].label).toBe('Status')
110
+ expect(facets[1].label).toBe('Created Date')
111
+ expect(facets[2].label).toBe('Expiry Date')
112
+ })
113
+
114
+ it('honors CMS-provided facet labels', () => {
115
+ const facets = getAllFacets(emptyFilters, {
116
+ statusFacetLabel: 'Estado',
117
+ createdAtFacetLabel: 'Data de criação',
118
+ expiresAtFacetLabel: 'Data de validade',
119
+ })
120
+ expect(facets[0].label).toBe('Estado')
121
+ expect(facets[1].label).toBe('Data de criação')
122
+ expect(facets[2].label).toBe('Data de validade')
123
+ })
124
+
125
+ it('honors CMS-provided status option labels', () => {
126
+ const facets = getAllFacets(emptyFilters, {
127
+ statusCmsLabels: { draftStatus: 'Rascunho', approvedStatus: 'Aprovado' },
128
+ })
129
+ if (facets[0].__typename !== 'StoreFacetBoolean') {
130
+ throw new Error('expected facets[0] to be StoreFacetBoolean')
131
+ }
132
+ const draftValue = facets[0].values.find((v) => v.value === 'Draft')
133
+ const approvedValue = facets[0].values.find((v) => v.value === 'Approved')
134
+ const declinedValue = facets[0].values.find((v) => v.value === 'Declined')
135
+ expect(draftValue?.label).toBe('Rascunho')
136
+ expect(approvedValue?.label).toBe('Aprovado')
137
+ // Statuses without a CMS override keep their default English label.
138
+ expect(declinedValue?.label).toBe('Declined')
139
+ })
106
140
  })
107
141
 
108
142
  describe('hasActiveFilters', () => {
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+
5
+ import '@testing-library/jest-dom/vitest'
6
+ import { cleanup, render, screen } from '@testing-library/react'
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
8
+
9
+ const mockUseAccountPageData = vi.hoisted(() => vi.fn())
10
+ const mockUseScreenResize = vi.hoisted(() => vi.fn())
11
+ const mockUseSession = vi.hoisted(() => vi.fn())
12
+ const mockUseUI = vi.hoisted(() => vi.fn())
13
+
14
+ vi.mock('src/sdk/account/accountPageContext', () => ({
15
+ useAccountPageData: mockUseAccountPageData,
16
+ }))
17
+ vi.mock('src/sdk/ui/useScreenResize', () => ({
18
+ default: mockUseScreenResize,
19
+ }))
20
+ vi.mock('src/sdk/session', () => ({ useSession: mockUseSession }))
21
+ vi.mock('@faststore/ui', async () => {
22
+ const actual =
23
+ await vi.importActual<typeof import('@faststore/ui')>('@faststore/ui')
24
+ return {
25
+ ...actual,
26
+ useUI: mockUseUI,
27
+ }
28
+ })
29
+
30
+ import AccountListQuotes from 'src/components/sections/Account/AccountListQuotes'
31
+
32
+ afterEach(cleanup)
33
+
34
+ function baseFilters(overrides: Record<string, unknown> = {}) {
35
+ return {
36
+ page: 1,
37
+ status: [],
38
+ createdAtFrom: '',
39
+ createdAtTo: '',
40
+ expiresAtFrom: '',
41
+ expiresAtTo: '',
42
+ label: '',
43
+ ...overrides,
44
+ }
45
+ }
46
+
47
+ describe('AccountListQuotes', () => {
48
+ beforeEach(() => {
49
+ mockUseScreenResize.mockReturnValue({ isDesktop: true })
50
+ mockUseSession.mockReturnValue({
51
+ locale: 'en-US',
52
+ currency: { code: 'USD' },
53
+ })
54
+ mockUseUI.mockReturnValue({ openFilter: vi.fn(), filter: false })
55
+ })
56
+
57
+ it('renders nothing when the page has no quote data', () => {
58
+ mockUseAccountPageData.mockReturnValueOnce({})
59
+
60
+ const { container } = render(<AccountListQuotes />)
61
+
62
+ expect(container.firstChild).toBeNull()
63
+ })
64
+
65
+ it('reads accountPageData and renders the default English page title', () => {
66
+ mockUseAccountPageData.mockReturnValueOnce({
67
+ listQuotes: { list: [] },
68
+ total: 0,
69
+ perPage: 25,
70
+ filters: baseFilters(),
71
+ })
72
+
73
+ render(<AccountListQuotes />)
74
+
75
+ expect(screen.getByText('Quotes')).toBeInTheDocument()
76
+ expect(screen.getByText("You don't have any quotes")).toBeInTheDocument()
77
+ })
78
+
79
+ it('applies CMS-provided label overrides', () => {
80
+ mockUseAccountPageData.mockReturnValueOnce({
81
+ listQuotes: { list: [] },
82
+ total: 0,
83
+ perPage: 25,
84
+ filters: baseFilters(),
85
+ })
86
+
87
+ render(
88
+ <AccountListQuotes
89
+ pageTitle="Cotações"
90
+ noQuotesLabel="Você não tem cotações"
91
+ />
92
+ )
93
+
94
+ expect(screen.getByText('Cotações')).toBeInTheDocument()
95
+ expect(screen.getByText('Você não tem cotações')).toBeInTheDocument()
96
+ })
97
+
98
+ it('forwards a CMS-provided quote status label down to the rendered rows', () => {
99
+ mockUseAccountPageData.mockReturnValueOnce({
100
+ listQuotes: {
101
+ list: [
102
+ {
103
+ id: 'q-1',
104
+ status: 'Approved',
105
+ label: null,
106
+ createdAt: '2026-01-01T10:00:00Z',
107
+ expiresAt: '2099-01-01T00:00:00Z',
108
+ amount: 100,
109
+ createdBy: null,
110
+ },
111
+ ],
112
+ },
113
+ total: 1,
114
+ perPage: 25,
115
+ filters: baseFilters(),
116
+ })
117
+
118
+ render(<AccountListQuotes approvedStatus="Aprovado" />)
119
+
120
+ expect(screen.getByText('Aprovado')).toBeInTheDocument()
121
+ })
122
+ })
@@ -21,6 +21,21 @@ describe('getDefaultMyAccountSections', () => {
21
21
  expect(getDefaultMyAccountSections('myAccountWishlist')).toEqual([])
22
22
  })
23
23
 
24
+ it('returns AccountNavigation and AccountListQuotes for myAccountQuotes', () => {
25
+ expect(getDefaultMyAccountSections('myAccountQuotes')).toEqual([
26
+ {
27
+ name: 'AccountNavigation',
28
+ $componentKey: 'AccountNavigation',
29
+ data: {},
30
+ },
31
+ {
32
+ name: 'AccountListQuotes',
33
+ $componentKey: 'AccountListQuotes',
34
+ data: {},
35
+ },
36
+ ])
37
+ })
38
+
24
39
  /**
25
40
  * The order-details defaults must mirror the render order of the
26
41
  * `@deprecated OrderDetails.tsx` component, which is the design reference
@@ -87,6 +102,12 @@ describe('withDefaultMyAccountSections', () => {
87
102
  )
88
103
  })
89
104
 
105
+ it('falls back to AccountNavigation + AccountListQuotes when myAccountQuotes has no CMS content yet', () => {
106
+ expect(withDefaultMyAccountSections('myAccountQuotes', [])).toEqual(
107
+ getDefaultMyAccountSections('myAccountQuotes')
108
+ )
109
+ })
110
+
90
111
  it('falls back to [] for store types when sections are empty', () => {
91
112
  expect(withDefaultMyAccountSections('myAccountWishlist', null)).toEqual([])
92
113
  })