@faststore/core 4.9.0-dev.2 → 4.9.0-dev.4
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/.turbo/turbo-generate.log +3 -3
- package/.turbo/turbo-test.log +130 -125
- package/CHANGELOG.md +10 -0
- package/cms/faststore/my-account/components/cms_component__accountlistquotes.jsonc +240 -0
- package/cms/faststore/my-account/components/cms_component__accountnavigation.jsonc +76 -0
- package/cms/faststore/my-account/pages/cms_content_type__myaccountquotes.jsonc +18 -0
- package/package.json +6 -6
- package/src/components/account/Drawer/OrganizationDrawer/ContractSwitcher/ContractSwitcher.tsx +129 -26
- package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerHeader.tsx +7 -1
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotes.tsx +42 -11
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountListQuotesTable.tsx +45 -15
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountListQuotesTable/MyAccountQuoteStatusBadge.tsx +11 -3
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesFilterSlider/MyAccountQuotesFilterSlider.tsx +23 -0
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesSelectedTags/MyAccountQuotesSelectedTags.tsx +35 -11
- package/src/components/account/quotes/MyAccountListQuotes/MyAccountQuotesStatusSelector/MyAccountQuotesStatusSelector.tsx +27 -10
- package/src/components/account/quotes/MyAccountListQuotes/index.ts +2 -0
- package/src/components/account/quotes/MyAccountListQuotes/quoteFilters.ts +16 -6
- package/src/components/account/quotes/MyAccountListQuotes/quotesLabels.ts +163 -0
- package/src/components/cms/account/Components.ts +2 -0
- package/src/components/sections/Account/AccountListQuotes.tsx +141 -0
- package/src/pages/pvt/account/quotes/index.tsx +59 -40
- package/src/sdk/account/accountPageContext.ts +17 -0
- package/src/sdk/account/getMyAccountRoutes.ts +17 -0
- package/src/sdk/account/resolveNonBlankLabels.ts +36 -0
- package/src/server/cms/myAccountDefaultSections.ts +2 -0
- package/src/utils/quoteStatus.ts +81 -0
- package/test/components/account/ContractSwitcher.browser.test.tsx +163 -0
- package/test/components/account/OrganizationDrawer.browser.test.tsx +4 -0
- package/test/components/account/OrganizationDrawerHeader.browser.test.tsx +44 -1
- package/test/components/quotes/MyAccountListQuotes.test.tsx +36 -2
- package/test/components/quotes/MyAccountListQuotesTable.test.tsx +68 -0
- package/test/components/quotes/MyAccountQuoteStatusBadge.test.tsx +25 -0
- package/test/components/quotes/MyAccountQuotesFilterSlider.test.tsx +52 -11
- package/test/components/quotes/MyAccountQuotesSelectedTags.test.tsx +32 -0
- package/test/components/quotes/MyAccountQuotesStatusSelector.test.tsx +100 -0
- package/test/components/quotes/quoteFilters.test.ts +34 -0
- package/test/components/sections/Account/AccountListQuotes.browser.test.tsx +122 -0
- 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
|
})
|