@carto/ps-react-ui 4.9.1 → 4.11.1
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/dist/category-Dnd2_j0x.js +719 -0
- package/dist/category-Dnd2_j0x.js.map +1 -0
- package/dist/change-column-DjjwoPt1.js +1143 -0
- package/dist/change-column-DjjwoPt1.js.map +1 -0
- package/dist/chat.js +1507 -0
- package/dist/chat.js.map +1 -0
- package/dist/components.js +122 -120
- package/dist/components.js.map +1 -1
- package/dist/copy-button-DGL1tyli.js +26 -0
- package/dist/copy-button-DGL1tyli.js.map +1 -0
- package/dist/{data-zoom-layout-0QSptXG_.js → data-zoom-layout-CkVnm6ej.js} +3 -3
- package/dist/{data-zoom-layout-0QSptXG_.js.map → data-zoom-layout-CkVnm6ej.js.map} +1 -1
- package/dist/{download-config-CzmjOT2T.js → download-config-oJIFZ2WC.js} +9 -8
- package/dist/{download-config-CzmjOT2T.js.map → download-config-oJIFZ2WC.js.map} +1 -1
- package/dist/{png-item-CS4z1iSH.js → png-item-BE9uEqlD.js} +2 -2
- package/dist/png-item-BE9uEqlD.js.map +1 -0
- package/dist/{spread-Y9R1f5dm.js → spread-DYNpzgh_.js} +10 -11
- package/dist/{spread-Y9R1f5dm.js.map → spread-DYNpzgh_.js.map} +1 -1
- package/dist/{table-CQCAnDLb.js → table-C9IMbTr0.js} +50 -53
- package/dist/table-C9IMbTr0.js.map +1 -0
- package/dist/types/chat/bubbles/chat-error-message.d.ts +2 -0
- package/dist/types/chat/bubbles/chat-suggestion-button.d.ts +2 -0
- package/dist/types/chat/bubbles/chat-user-message.d.ts +2 -0
- package/dist/types/chat/bubbles/index.d.ts +4 -0
- package/dist/types/chat/const.d.ts +4 -0
- package/dist/types/chat/containers/chat-content.d.ts +2 -0
- package/dist/types/chat/containers/chat-footer.d.ts +2 -0
- package/dist/types/chat/containers/chat-header.d.ts +2 -0
- package/dist/types/chat/containers/chat-starter.d.ts +2 -0
- package/dist/types/chat/containers/index.d.ts +4 -0
- package/dist/types/chat/containers/styles.d.ts +93 -0
- package/dist/types/chat/feedback/chat-loader.d.ts +2 -0
- package/dist/types/chat/feedback/chat-rating-action.d.ts +2 -0
- package/dist/types/chat/feedback/chat-thinking.d.ts +2 -0
- package/dist/types/chat/feedback/chat-tool-code-area.d.ts +2 -0
- package/dist/types/chat/feedback/chat-tool-full-view-dialog.d.ts +2 -0
- package/dist/types/chat/feedback/chat-tool-group.d.ts +2 -0
- package/dist/types/chat/feedback/chat-tool-trace.d.ts +3 -0
- package/dist/types/chat/feedback/get-tool-label.d.ts +2 -0
- package/dist/types/chat/feedback/index.d.ts +8 -0
- package/dist/types/chat/feedback/styles.d.ts +211 -0
- package/dist/types/chat/index.d.ts +20 -0
- package/dist/types/chat/types.d.ts +184 -0
- package/dist/types/chat/use-typewriter.d.ts +30 -0
- package/dist/types/components/copy-button/copy-button.d.ts +2 -0
- package/dist/types/components/copy-button/types.d.ts +6 -0
- package/dist/types/components/index.d.ts +2 -0
- package/dist/types/widgets/actions/brush-toggle/style.d.ts +1 -1
- package/dist/types/widgets/actions/shared/styles.d.ts +1 -1
- package/dist/types/widgets/actions/zoom-toggle/style.d.ts +1 -1
- package/dist/types/widgets/echart/types.d.ts +1 -1
- package/dist/types/widgets/toolbar-actions/styles.d.ts +1 -1
- package/dist/types/widgets-v2/actions/brush-toggle/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/change-column/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/fullscreen/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/index.d.ts +1 -0
- package/dist/types/widgets-v2/actions/lock-selection/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/relative-data/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/searcher/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/show-all/index.d.ts +2 -0
- package/dist/types/widgets-v2/actions/show-all/labels.d.ts +5 -0
- package/dist/types/widgets-v2/actions/show-all/show-all.d.ts +33 -0
- package/dist/types/widgets-v2/actions/show-all/style.d.ts +8 -0
- package/dist/types/widgets-v2/actions/stack-toggle/style.d.ts +1 -1
- package/dist/types/widgets-v2/actions/zoom-toggle/style.d.ts +1 -1
- package/dist/types/widgets-v2/category/category-ui.d.ts +9 -2
- package/dist/types/widgets-v2/category/category.d.ts +9 -2
- package/dist/types/widgets-v2/category/components/category-row-other.d.ts +19 -6
- package/dist/types/widgets-v2/category/style.d.ts +21 -2
- package/dist/types/widgets-v2/category/types.d.ts +2 -0
- package/dist/types/widgets-v2/index.d.ts +3 -2
- package/dist/types/widgets-v2/selection-summary/labels.d.ts +7 -2
- package/dist/types/widgets-v2/selection-summary/selection-summary.d.ts +13 -6
- package/dist/types/widgets-v2/selection-summary/style.d.ts +15 -0
- package/dist/widgets/actions.js +115 -114
- package/dist/widgets/actions.js.map +1 -1
- package/dist/widgets/bar.js +1 -1
- package/dist/widgets/category.js +9 -8
- package/dist/widgets/category.js.map +1 -1
- package/dist/widgets/formula.js +11 -10
- package/dist/widgets/formula.js.map +1 -1
- package/dist/widgets/histogram.js +7 -6
- package/dist/widgets/histogram.js.map +1 -1
- package/dist/widgets/markdown.js +9 -8
- package/dist/widgets/markdown.js.map +1 -1
- package/dist/widgets/pie.js +1 -1
- package/dist/widgets/scatterplot.js +1 -1
- package/dist/widgets/spread.js +9 -8
- package/dist/widgets/spread.js.map +1 -1
- package/dist/widgets/table.js +17 -16
- package/dist/widgets/table.js.map +1 -1
- package/dist/widgets/timeseries.js +1 -1
- package/dist/widgets/utils.js +1 -1
- package/dist/widgets/wrapper.js +3 -2
- package/dist/widgets/wrapper.js.map +1 -1
- package/dist/widgets-v2/actions.js +41 -37
- package/dist/widgets-v2/bar.js +9 -10
- package/dist/widgets-v2/bar.js.map +1 -1
- package/dist/widgets-v2/category.js +25 -26
- package/dist/widgets-v2/category.js.map +1 -1
- package/dist/widgets-v2/formula.js +3 -3
- package/dist/widgets-v2/histogram.js +11 -13
- package/dist/widgets-v2/histogram.js.map +1 -1
- package/dist/widgets-v2/markdown.js +26 -27
- package/dist/widgets-v2/markdown.js.map +1 -1
- package/dist/widgets-v2/pie.js +8 -10
- package/dist/widgets-v2/pie.js.map +1 -1
- package/dist/widgets-v2/scatterplot.js +10 -12
- package/dist/widgets-v2/scatterplot.js.map +1 -1
- package/dist/widgets-v2/spread.js +15 -16
- package/dist/widgets-v2/spread.js.map +1 -1
- package/dist/widgets-v2/table.js +39 -40
- package/dist/widgets-v2/table.js.map +1 -1
- package/dist/widgets-v2/timeseries.js +9 -11
- package/dist/widgets-v2/timeseries.js.map +1 -1
- package/dist/widgets-v2/utils.js +1 -1
- package/dist/widgets-v2.js +284 -282
- package/dist/widgets-v2.js.map +1 -1
- package/package.json +5 -1
- package/src/chat/bubbles/chat-agent-message.test.tsx +30 -0
- package/src/chat/bubbles/chat-agent-message.tsx +11 -0
- package/src/chat/bubbles/chat-error-message.test.tsx +40 -0
- package/src/chat/bubbles/chat-error-message.tsx +47 -0
- package/src/chat/bubbles/chat-suggestion-button.test.tsx +24 -0
- package/src/chat/bubbles/chat-suggestion-button.tsx +27 -0
- package/src/chat/bubbles/chat-user-message.test.tsx +27 -0
- package/src/chat/bubbles/chat-user-message.tsx +27 -0
- package/src/chat/bubbles/index.ts +4 -0
- package/src/chat/bubbles/styles.ts +148 -0
- package/src/chat/const.ts +4 -0
- package/src/chat/containers/chat-content.test.tsx +269 -0
- package/src/chat/containers/chat-content.tsx +142 -0
- package/src/chat/containers/chat-footer.test.tsx +34 -0
- package/src/chat/containers/chat-footer.tsx +78 -0
- package/src/chat/containers/chat-header.test.tsx +28 -0
- package/src/chat/containers/chat-header.tsx +29 -0
- package/src/chat/containers/chat-starter.test.tsx +32 -0
- package/src/chat/containers/chat-starter.tsx +75 -0
- package/src/chat/containers/index.ts +4 -0
- package/src/chat/containers/styles.ts +96 -0
- package/src/chat/feedback/chat-actions-container.test.tsx +64 -0
- package/src/chat/feedback/chat-actions-container.tsx +7 -0
- package/src/chat/feedback/chat-loader.test.tsx +10 -0
- package/src/chat/feedback/chat-loader.tsx +31 -0
- package/src/chat/feedback/chat-rating-action.tsx +43 -0
- package/src/chat/feedback/chat-thinking.test.tsx +15 -0
- package/src/chat/feedback/chat-thinking.tsx +23 -0
- package/src/chat/feedback/chat-tool-code-area.test.tsx +23 -0
- package/src/chat/feedback/chat-tool-code-area.tsx +71 -0
- package/src/chat/feedback/chat-tool-full-view-dialog.test.tsx +39 -0
- package/src/chat/feedback/chat-tool-full-view-dialog.tsx +121 -0
- package/src/chat/feedback/chat-tool-group.test.tsx +84 -0
- package/src/chat/feedback/chat-tool-group.tsx +156 -0
- package/src/chat/feedback/chat-tool-trace.test.tsx +81 -0
- package/src/chat/feedback/chat-tool-trace.tsx +192 -0
- package/src/chat/feedback/get-tool-label.test.tsx +91 -0
- package/src/chat/feedback/get-tool-label.ts +13 -0
- package/src/chat/feedback/index.ts +8 -0
- package/src/chat/feedback/styles.ts +229 -0
- package/src/chat/index.ts +59 -0
- package/src/chat/types.ts +215 -0
- package/src/chat/use-typewriter.test.tsx +38 -0
- package/src/chat/use-typewriter.ts +82 -0
- package/src/components/copy-button/copy-button.test.tsx +41 -0
- package/src/components/copy-button/copy-button.tsx +31 -0
- package/src/components/copy-button/types.ts +10 -0
- package/src/components/index.ts +3 -0
- package/src/widgets/echart/types.ts +1 -1
- package/src/widgets-v2/actions/brush-toggle/brush-toggle.tsx +1 -1
- package/src/widgets-v2/actions/change-column/sortable-column-item.tsx +1 -1
- package/src/widgets-v2/actions/download/download.tsx +1 -1
- package/src/widgets-v2/actions/download/icons.tsx +1 -1
- package/src/widgets-v2/actions/fullscreen/fullscreen.tsx +3 -3
- package/src/widgets-v2/actions/index.ts +8 -0
- package/src/widgets-v2/actions/lock-selection/lock-selection.tsx +2 -2
- package/src/widgets-v2/actions/relative-data/relative-data.tsx +1 -1
- package/src/widgets-v2/actions/searcher/searcher-toggle.tsx +1 -1
- package/src/widgets-v2/actions/searcher/searcher.tsx +2 -2
- package/src/widgets-v2/actions/show-all/index.ts +7 -0
- package/src/widgets-v2/actions/show-all/labels.ts +8 -0
- package/src/widgets-v2/actions/show-all/show-all.test.tsx +50 -0
- package/src/widgets-v2/actions/show-all/show-all.tsx +72 -0
- package/src/widgets-v2/actions/show-all/style.ts +8 -0
- package/src/widgets-v2/actions/stack-toggle/stack-toggle.tsx +1 -1
- package/src/widgets-v2/actions/zoom-toggle/zoom-toggle.tsx +1 -1
- package/src/widgets-v2/category/category-ui.test.tsx +26 -10
- package/src/widgets-v2/category/category-ui.tsx +13 -3
- package/src/widgets-v2/category/category.test.tsx +4 -4
- package/src/widgets-v2/category/category.tsx +10 -1
- package/src/widgets-v2/category/components/category-row-other.test.tsx +36 -7
- package/src/widgets-v2/category/components/category-row-other.tsx +64 -13
- package/src/widgets-v2/category/style.ts +35 -4
- package/src/widgets-v2/category/types.ts +2 -0
- package/src/widgets-v2/index.ts +3 -0
- package/src/widgets-v2/selection-summary/labels.ts +8 -4
- package/src/widgets-v2/selection-summary/selection-summary.test.tsx +15 -9
- package/src/widgets-v2/selection-summary/selection-summary.tsx +42 -22
- package/src/widgets-v2/selection-summary/style.ts +15 -0
- package/src/widgets-v2/table/table-ui.tsx +4 -4
- package/src/widgets-v2/toolbox/toolbox.tsx +1 -1
- package/src/widgets-v2/wrapper/widget-wrapper.tsx +1 -1
- package/dist/category-DwaeYjpX.js +0 -656
- package/dist/category-DwaeYjpX.js.map +0 -1
- package/dist/change-column-B4IT0rh6.js +0 -1110
- package/dist/change-column-B4IT0rh6.js.map +0 -1
- package/dist/png-item-CS4z1iSH.js.map +0 -1
- package/dist/table-CQCAnDLb.js.map +0 -1
|
@@ -40,7 +40,7 @@ export interface CategoryProps {
|
|
|
40
40
|
/** Per-series metadata. Enables the legend + overrides palette per index. */
|
|
41
41
|
series?: readonly CategorySeriesConfig[]
|
|
42
42
|
/**
|
|
43
|
-
* Cap visible rows; overflow folds into "
|
|
43
|
+
* Cap visible rows; overflow folds into an "Others <count>" row. Default 20
|
|
44
44
|
* (when omitted). Pass `0` to swap the cap for a scrollable viewport
|
|
45
45
|
* (composers use this when the user opens the SearcherToggle —
|
|
46
46
|
* `maxItems = searcherOpen ? 0 : userMaxItems`). Pass `null` to disable
|
|
@@ -49,6 +49,13 @@ export interface CategoryProps {
|
|
|
49
49
|
maxItems?: number | null
|
|
50
50
|
/** Labels for the "Other" overflow row. */
|
|
51
51
|
labels?: CategoryLabels
|
|
52
|
+
/**
|
|
53
|
+
* When provided, the "Other" overflow row becomes a button that fires this
|
|
54
|
+
* callback. Composers wire it to expand the widget (e.g. flip the
|
|
55
|
+
* `show-all` flag) so every category is shown. Forwarded to
|
|
56
|
+
* {@link CategoryUI}.
|
|
57
|
+
*/
|
|
58
|
+
onShowAll?: () => void
|
|
52
59
|
/**
|
|
53
60
|
* Manual override for the bar-width denominator. When omitted, the
|
|
54
61
|
* bridge auto-fills from the widget store's `rawData` so bar widths
|
|
@@ -85,6 +92,7 @@ export function Category({
|
|
|
85
92
|
series,
|
|
86
93
|
maxItems,
|
|
87
94
|
labels,
|
|
95
|
+
onShowAll,
|
|
88
96
|
maxOverride,
|
|
89
97
|
size,
|
|
90
98
|
stacked,
|
|
@@ -113,6 +121,7 @@ export function Category({
|
|
|
113
121
|
series={series}
|
|
114
122
|
maxItems={maxItems}
|
|
115
123
|
labels={labels}
|
|
124
|
+
onShowAll={onShowAll}
|
|
116
125
|
maxOverride={effectiveMaxOverride}
|
|
117
126
|
size={size}
|
|
118
127
|
stacked={stacked}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest'
|
|
2
|
-
import { render, screen } from '@testing-library/react'
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
3
3
|
import { CategoryRowOther } from './category-row-other'
|
|
4
4
|
|
|
5
5
|
describe('<CategoryRowOther>', () => {
|
|
6
|
-
it('renders the default
|
|
6
|
+
it('renders the default label + bare count (no parentheses)', () => {
|
|
7
7
|
render(<CategoryRowOther hiddenCount={7} />)
|
|
8
|
-
expect(screen.getByText('
|
|
9
|
-
expect(screen.getByText('
|
|
8
|
+
expect(screen.getByText('Others')).toBeTruthy()
|
|
9
|
+
expect(screen.getByText('7')).toBeTruthy()
|
|
10
|
+
expect(screen.queryByText('(7 more)')).toBeNull()
|
|
10
11
|
})
|
|
11
12
|
|
|
12
13
|
it('honors custom labels with {count} placeholder', () => {
|
|
@@ -18,11 +19,39 @@ describe('<CategoryRowOther>', () => {
|
|
|
18
19
|
/>,
|
|
19
20
|
)
|
|
20
21
|
expect(screen.getByText('Hidden')).toBeTruthy()
|
|
21
|
-
expect(screen.getByText('
|
|
22
|
+
expect(screen.getByText('+3 not shown')).toBeTruthy()
|
|
22
23
|
})
|
|
23
24
|
|
|
24
|
-
it('is not a button (
|
|
25
|
+
it('is not a button when onShowAll is omitted (static fallback)', () => {
|
|
25
26
|
const { container } = render(<CategoryRowOther hiddenCount={1} />)
|
|
26
27
|
expect(container.querySelectorAll('[role="button"]').length).toBe(0)
|
|
27
28
|
})
|
|
29
|
+
|
|
30
|
+
describe('when onShowAll is provided', () => {
|
|
31
|
+
it('renders as a button', () => {
|
|
32
|
+
render(<CategoryRowOther hiddenCount={4} onShowAll={vi.fn()} />)
|
|
33
|
+
expect(screen.getByRole('button')).toBeTruthy()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('fires onShowAll on click', () => {
|
|
37
|
+
const onShowAll = vi.fn()
|
|
38
|
+
render(<CategoryRowOther hiddenCount={4} onShowAll={onShowAll} />)
|
|
39
|
+
fireEvent.click(screen.getByRole('button'))
|
|
40
|
+
expect(onShowAll).toHaveBeenCalledTimes(1)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('fires onShowAll on Enter and Space', () => {
|
|
44
|
+
const onShowAll = vi.fn()
|
|
45
|
+
render(<CategoryRowOther hiddenCount={4} onShowAll={onShowAll} />)
|
|
46
|
+
const btn = screen.getByRole('button')
|
|
47
|
+
fireEvent.keyDown(btn, { key: 'Enter' })
|
|
48
|
+
fireEvent.keyDown(btn, { key: ' ' })
|
|
49
|
+
expect(onShowAll).toHaveBeenCalledTimes(2)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('is keyboard-focusable', () => {
|
|
53
|
+
render(<CategoryRowOther hiddenCount={4} onShowAll={vi.fn()} />)
|
|
54
|
+
expect(screen.getByRole('button').getAttribute('tabindex')).toBe('0')
|
|
55
|
+
})
|
|
56
|
+
})
|
|
28
57
|
})
|
|
@@ -1,33 +1,84 @@
|
|
|
1
|
+
import type { KeyboardEvent } from 'react'
|
|
1
2
|
import { Box, Typography } from '@mui/material'
|
|
3
|
+
import { Tooltip } from '../../../components'
|
|
2
4
|
import { styles } from '../style'
|
|
3
5
|
|
|
4
6
|
export interface CategoryRowOtherProps {
|
|
5
7
|
hiddenCount: number
|
|
6
|
-
/** Label for the overflow row. Defaults to `'
|
|
8
|
+
/** Label for the overflow row. Defaults to `'Others'`. */
|
|
7
9
|
otherLabel?: string
|
|
8
|
-
/** Count text with `{count}` placeholder. Defaults to `'{count}
|
|
10
|
+
/** Count text with `{count}` placeholder. Defaults to `'{count}'`. */
|
|
9
11
|
otherCountLabel?: string
|
|
12
|
+
/**
|
|
13
|
+
* When provided, the row becomes a button: clicking it (or Enter/Space)
|
|
14
|
+
* fires `onShowAll`, expanding the widget to reveal every category. When
|
|
15
|
+
* omitted the row renders as a static, non-interactive summary.
|
|
16
|
+
*/
|
|
17
|
+
onShowAll?: () => void
|
|
18
|
+
/** Tooltip shown over the button form. Defaults to `'Show all'`. */
|
|
19
|
+
showAllLabel?: string
|
|
10
20
|
}
|
|
11
21
|
|
|
12
22
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
23
|
+
* Overflow summary row rendered after the last visible category row when
|
|
24
|
+
* `data.length > maxItems`. Shows `Others <count>` (count muted, no
|
|
25
|
+
* parentheses).
|
|
26
|
+
*
|
|
27
|
+
* When `onShowAll` is supplied the row is an interactive button — clicking
|
|
28
|
+
* it (or Enter/Space) expands the widget to show all categories, mirroring
|
|
29
|
+
* the Searcher's full-list view. Without `onShowAll` it stays a static,
|
|
30
|
+
* non-interactive summary (backward-compatible default).
|
|
16
31
|
*/
|
|
17
32
|
export function CategoryRowOther({
|
|
18
33
|
hiddenCount,
|
|
19
|
-
otherLabel = '
|
|
20
|
-
otherCountLabel = '{count}
|
|
34
|
+
otherLabel = 'Others',
|
|
35
|
+
otherCountLabel = '{count}',
|
|
36
|
+
onShowAll,
|
|
37
|
+
showAllLabel = 'Show all',
|
|
21
38
|
}: CategoryRowOtherProps) {
|
|
22
39
|
const countText = otherCountLabel.replace('{count}', String(hiddenCount))
|
|
40
|
+
|
|
41
|
+
const count = (
|
|
42
|
+
<Typography variant='body2' sx={styles.otherCount}>
|
|
43
|
+
{countText}
|
|
44
|
+
</Typography>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if (!onShowAll) {
|
|
48
|
+
return (
|
|
49
|
+
<Box sx={styles.otherRow}>
|
|
50
|
+
<Typography variant='body2' sx={styles.otherLabel}>
|
|
51
|
+
{otherLabel}
|
|
52
|
+
</Typography>
|
|
53
|
+
{count}
|
|
54
|
+
</Box>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const handleKey = (e: KeyboardEvent<HTMLSpanElement>): void => {
|
|
59
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
60
|
+
e.preventDefault()
|
|
61
|
+
onShowAll()
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Only the label is the click target; the count stays static beside it.
|
|
23
66
|
return (
|
|
24
67
|
<Box sx={styles.otherRow}>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
68
|
+
<Tooltip title={showAllLabel}>
|
|
69
|
+
<Typography
|
|
70
|
+
variant='body2'
|
|
71
|
+
component='span'
|
|
72
|
+
role='button'
|
|
73
|
+
tabIndex={0}
|
|
74
|
+
onClick={onShowAll}
|
|
75
|
+
onKeyDown={handleKey}
|
|
76
|
+
sx={{ ...styles.otherLabel, ...styles.otherLabelButton }}
|
|
77
|
+
>
|
|
78
|
+
{otherLabel}
|
|
79
|
+
</Typography>
|
|
80
|
+
</Tooltip>
|
|
81
|
+
{count}
|
|
31
82
|
</Box>
|
|
32
83
|
)
|
|
33
84
|
}
|
|
@@ -78,6 +78,13 @@ export const styles = {
|
|
|
78
78
|
gap: 1,
|
|
79
79
|
py: 0.5,
|
|
80
80
|
},
|
|
81
|
+
// Layered on `list` only in scroll mode (`maxItems === 0`): right padding
|
|
82
|
+
// so the bars + right-aligned values clear the scrollbar. Right side only
|
|
83
|
+
// — the left edge stays at 0 so content stays aligned with the capped
|
|
84
|
+
// view. `overflowY` + the dynamic `maxHeight` remain on the inline style.
|
|
85
|
+
listScroll: {
|
|
86
|
+
pr: 1,
|
|
87
|
+
},
|
|
81
88
|
|
|
82
89
|
// ── Single-series row ─────────────────────────────────────────────
|
|
83
90
|
// v1 layout: label + value share a top flex row (justify-between); the
|
|
@@ -266,20 +273,44 @@ export const styles = {
|
|
|
266
273
|
},
|
|
267
274
|
|
|
268
275
|
// ── "Other" overflow row ──────────────────────────────────────────
|
|
276
|
+
// Left-aligned `Others <count>` — the label sits next to a muted count
|
|
277
|
+
// (no parentheses). Only the label is the click target when interactive;
|
|
278
|
+
// the count is always static text beside it.
|
|
269
279
|
otherRow: {
|
|
270
280
|
display: 'flex',
|
|
271
|
-
justifyContent: 'space-between',
|
|
272
281
|
alignItems: 'center',
|
|
282
|
+
gap: 1,
|
|
273
283
|
py: 0.5,
|
|
274
284
|
px: 0.5,
|
|
275
285
|
},
|
|
286
|
+
// Interactive affordances layered onto the "Others" label only (cursor,
|
|
287
|
+
// hover tint, focus ring). A negative LEFT margin + matching padding keep
|
|
288
|
+
// the hover pill breathing without shifting the label's text edge (it
|
|
289
|
+
// stays aligned with the category rows above). No right margin, so the
|
|
290
|
+
// row `gap` is preserved between the pill and the count.
|
|
291
|
+
otherLabelButton: {
|
|
292
|
+
cursor: 'pointer',
|
|
293
|
+
userSelect: 'none',
|
|
294
|
+
borderRadius: 1,
|
|
295
|
+
px: 0.5,
|
|
296
|
+
ml: -0.5,
|
|
297
|
+
transition: 'background-color 80ms ease',
|
|
298
|
+
'&:hover': {
|
|
299
|
+
bgcolor: 'action.hover',
|
|
300
|
+
},
|
|
301
|
+
'&:focus-visible': {
|
|
302
|
+
outline: '2px solid',
|
|
303
|
+
outlineColor: 'primary.main',
|
|
304
|
+
outlineOffset: 2,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
276
307
|
otherLabel: {
|
|
277
|
-
|
|
278
|
-
color: 'text.secondary',
|
|
308
|
+
color: 'text.primary',
|
|
279
309
|
fontWeight: 'medium',
|
|
280
310
|
},
|
|
281
311
|
otherCount: {
|
|
282
|
-
color: 'text.
|
|
312
|
+
color: 'text.secondary',
|
|
313
|
+
fontVariantNumeric: 'tabular-nums',
|
|
283
314
|
},
|
|
284
315
|
// `bar` / `barFill` are size-keyed (`Record<CategorySize, SxProps>`);
|
|
285
316
|
// every other entry is a single `SxProps<Theme>`. The mixed shape means
|
package/src/widgets-v2/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
Searcher,
|
|
23
23
|
SearcherToggle,
|
|
24
24
|
StackToggle,
|
|
25
|
+
ShowAllToggle,
|
|
25
26
|
ZoomToggle,
|
|
26
27
|
BrushToggle,
|
|
27
28
|
RelativeData,
|
|
@@ -83,6 +84,7 @@ export const Widget = {
|
|
|
83
84
|
Searcher,
|
|
84
85
|
SearcherToggle,
|
|
85
86
|
StackToggle,
|
|
87
|
+
ShowAllToggle,
|
|
86
88
|
ZoomToggle,
|
|
87
89
|
BrushToggle,
|
|
88
90
|
RelativeData,
|
|
@@ -149,6 +151,7 @@ export type {
|
|
|
149
151
|
SearcherProps,
|
|
150
152
|
SearcherToggleProps,
|
|
151
153
|
StackToggleProps,
|
|
154
|
+
ShowAllToggleProps,
|
|
152
155
|
ZoomToggleProps,
|
|
153
156
|
BrushToggleProps,
|
|
154
157
|
RelativeDataProps,
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
|
|
1
3
|
export interface SelectionSummaryLabels {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Optional custom renderer for the count. Defaults to the built-in two-tone
|
|
6
|
+
* `selected / total` rendering. Receives the already-resolved displayed
|
|
7
|
+
* number (which equals `total` when nothing is selected).
|
|
8
|
+
*/
|
|
9
|
+
summary?: (selected: number, total: number) => ReactNode
|
|
4
10
|
clear: string
|
|
5
11
|
}
|
|
6
12
|
|
|
7
13
|
export const DEFAULT_SELECTION_SUMMARY_LABELS: SelectionSummaryLabels = {
|
|
8
|
-
allSelected: 'All selected',
|
|
9
|
-
selections: (count) => `${count} selected`,
|
|
10
14
|
clear: 'Clear',
|
|
11
15
|
}
|
|
@@ -9,15 +9,22 @@ describe('<SelectionSummary>', () => {
|
|
|
9
9
|
expect(container.textContent).toBe('')
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
-
it('renders
|
|
12
|
+
it('renders nothing when total is omitted (undefined)', () => {
|
|
13
|
+
const { container } = render(<SelectionSummary count={0} />)
|
|
14
|
+
expect(container.textContent).toBe('')
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('renders total / total when nothing is selected (count === 0)', () => {
|
|
13
18
|
render(<SelectionSummary count={0} total={10} />)
|
|
14
|
-
expect(screen.
|
|
19
|
+
expect(screen.getByLabelText('10 / 10')).toBeTruthy()
|
|
20
|
+
// No Clear button when nothing is selected.
|
|
21
|
+
expect(screen.queryByText('Clear')).toBeNull()
|
|
15
22
|
})
|
|
16
23
|
|
|
17
|
-
it('renders
|
|
24
|
+
it('renders count / total + Clear when count > 0', () => {
|
|
18
25
|
const onClear = vi.fn()
|
|
19
26
|
render(<SelectionSummary count={3} total={10} onClear={onClear} />)
|
|
20
|
-
expect(screen.
|
|
27
|
+
expect(screen.getByLabelText('3 / 10')).toBeTruthy()
|
|
21
28
|
fireEvent.click(screen.getByText('Clear'))
|
|
22
29
|
expect(onClear).toHaveBeenCalledTimes(1)
|
|
23
30
|
})
|
|
@@ -27,24 +34,23 @@ describe('<SelectionSummary>', () => {
|
|
|
27
34
|
expect(screen.queryByText('Clear')).toBeNull()
|
|
28
35
|
})
|
|
29
36
|
|
|
30
|
-
it('honors custom
|
|
37
|
+
it('honors a custom summary renderer and clear label', () => {
|
|
31
38
|
render(
|
|
32
39
|
<SelectionSummary
|
|
33
40
|
count={3}
|
|
34
41
|
total={10}
|
|
35
42
|
labels={{
|
|
36
|
-
|
|
37
|
-
selections: (n) => `${n} pinned`,
|
|
43
|
+
summary: (selected, total) => `${selected} of ${total} pinned`,
|
|
38
44
|
clear: 'Reset',
|
|
39
45
|
}}
|
|
40
46
|
onClear={() => undefined}
|
|
41
47
|
/>,
|
|
42
48
|
)
|
|
43
|
-
expect(screen.getByText('3 pinned')).toBeTruthy()
|
|
49
|
+
expect(screen.getByText('3 of 10 pinned')).toBeTruthy()
|
|
44
50
|
expect(screen.getByText('Reset')).toBeTruthy()
|
|
45
51
|
})
|
|
46
52
|
|
|
47
|
-
it('renders the custom icon component when provided
|
|
53
|
+
it('renders the custom icon component when provided', () => {
|
|
48
54
|
const { container } = render(
|
|
49
55
|
<SelectionSummary count={0} total={10} icon={StarIcon} />,
|
|
50
56
|
)
|
|
@@ -9,9 +9,9 @@ import { styles } from './style'
|
|
|
9
9
|
export interface SelectionSummaryProps {
|
|
10
10
|
/** Number of currently selected items. */
|
|
11
11
|
count: number
|
|
12
|
-
/** Optional total — when 0 the component renders nothing (no data
|
|
12
|
+
/** Optional total — when `undefined` or `0` the component renders nothing (no data). */
|
|
13
13
|
total?: number
|
|
14
|
-
/** Clear callback.
|
|
14
|
+
/** Clear callback. The Clear control is shown only when `count > 0` and `onClear` is provided. */
|
|
15
15
|
onClear?: () => void
|
|
16
16
|
labels?: Partial<SelectionSummaryLabels>
|
|
17
17
|
icon?: ComponentType<SvgIconProps>
|
|
@@ -19,11 +19,18 @@ export interface SelectionSummaryProps {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
+
* Renders a `selected / total` count.
|
|
23
|
+
*
|
|
22
24
|
* Render rules:
|
|
23
|
-
* - `
|
|
24
|
-
* - `count === 0` →
|
|
25
|
-
*
|
|
26
|
-
* - `count > 0`
|
|
25
|
+
* - `!total` (undefined or 0) → render nothing (no data to summarize).
|
|
26
|
+
* - `count === 0` → display `total / total` (everything is implicitly in
|
|
27
|
+
* scope); no Clear button.
|
|
28
|
+
* - `count > 0` → display `count / total` + Clear (when `onClear` is given).
|
|
29
|
+
*
|
|
30
|
+
* The count is two-tone by default (selected number bold/`text.primary`,
|
|
31
|
+
* ` / total` in `text.secondary`). Pass `labels.summary` to render a custom
|
|
32
|
+
* node instead — it receives the already-resolved displayed number (which
|
|
33
|
+
* equals `total` when nothing is selected).
|
|
27
34
|
*/
|
|
28
35
|
export function SelectionSummary({
|
|
29
36
|
count,
|
|
@@ -33,26 +40,39 @@ export function SelectionSummary({
|
|
|
33
40
|
icon: Icon,
|
|
34
41
|
iconProps,
|
|
35
42
|
}: SelectionSummaryProps) {
|
|
36
|
-
if (total
|
|
43
|
+
if (!total) return null
|
|
37
44
|
const _labels = { ...DEFAULT_SELECTION_SUMMARY_LABELS, ...labels }
|
|
38
|
-
|
|
39
|
-
if (count === 0) {
|
|
40
|
-
return (
|
|
41
|
-
<Box sx={styles.root}>
|
|
42
|
-
{Icon ? <Icon fontSize='small' {...iconProps} /> : null}
|
|
43
|
-
<Typography variant='caption' sx={styles.label}>
|
|
44
|
-
{_labels.allSelected}
|
|
45
|
-
</Typography>
|
|
46
|
-
</Box>
|
|
47
|
-
)
|
|
48
|
-
}
|
|
45
|
+
const shown = count === 0 ? total : count
|
|
49
46
|
|
|
50
47
|
return (
|
|
51
48
|
<Box sx={styles.root}>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
{Icon ? <Icon fontSize='small' {...iconProps} /> : null}
|
|
50
|
+
<Box
|
|
51
|
+
component='span'
|
|
52
|
+
sx={styles.count}
|
|
53
|
+
aria-label={`${shown} / ${total}`}
|
|
54
|
+
>
|
|
55
|
+
{_labels.summary ? (
|
|
56
|
+
_labels.summary(shown, total)
|
|
57
|
+
) : (
|
|
58
|
+
<>
|
|
59
|
+
<Typography component='span' variant='caption' sx={styles.selected}>
|
|
60
|
+
{shown}
|
|
61
|
+
</Typography>
|
|
62
|
+
<Typography
|
|
63
|
+
component='span'
|
|
64
|
+
variant='caption'
|
|
65
|
+
sx={styles.separator}
|
|
66
|
+
>
|
|
67
|
+
/
|
|
68
|
+
</Typography>
|
|
69
|
+
<Typography component='span' variant='caption' sx={styles.total}>
|
|
70
|
+
{total}
|
|
71
|
+
</Typography>
|
|
72
|
+
</>
|
|
73
|
+
)}
|
|
74
|
+
</Box>
|
|
75
|
+
{count > 0 && onClear && (
|
|
56
76
|
<Button size='small' variant='text' onClick={onClear}>
|
|
57
77
|
{_labels.clear}
|
|
58
78
|
</Button>
|
|
@@ -10,6 +10,21 @@ export const styles = {
|
|
|
10
10
|
label: {
|
|
11
11
|
color: 'text.secondary',
|
|
12
12
|
},
|
|
13
|
+
count: {
|
|
14
|
+
display: 'inline-flex',
|
|
15
|
+
alignItems: 'baseline',
|
|
16
|
+
gap: 0.5,
|
|
17
|
+
},
|
|
18
|
+
selected: {
|
|
19
|
+
color: 'text.primary',
|
|
20
|
+
fontWeight: 600,
|
|
21
|
+
},
|
|
22
|
+
separator: {
|
|
23
|
+
color: 'text.secondary',
|
|
24
|
+
},
|
|
25
|
+
total: {
|
|
26
|
+
color: 'text.secondary',
|
|
27
|
+
},
|
|
13
28
|
clear: {
|
|
14
29
|
color: 'primary.main',
|
|
15
30
|
cursor: 'pointer',
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
type TableProps as MuiTableProps,
|
|
15
15
|
} from '@mui/material'
|
|
16
16
|
import type { TablePaginationActionsProps } from '@mui/material/TablePagination/TablePaginationActions'
|
|
17
|
-
import FirstPageIcon from '@mui/icons-material
|
|
18
|
-
import KeyboardArrowLeft from '@mui/icons-material
|
|
19
|
-
import KeyboardArrowRight from '@mui/icons-material
|
|
20
|
-
import LastPageIcon from '@mui/icons-material
|
|
17
|
+
import { FirstPage as FirstPageIcon } from '@mui/icons-material'
|
|
18
|
+
import { KeyboardArrowLeft } from '@mui/icons-material'
|
|
19
|
+
import { KeyboardArrowRight } from '@mui/icons-material'
|
|
20
|
+
import { LastPage as LastPageIcon } from '@mui/icons-material'
|
|
21
21
|
import { DEFAULT_TABLE_LABELS, type TableLabels } from './labels'
|
|
22
22
|
import {
|
|
23
23
|
DEFAULT_TABLE_PAGE_SIZE_OPTIONS,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
type SxProps,
|
|
23
23
|
type Theme,
|
|
24
24
|
} from '@mui/material'
|
|
25
|
-
import CloseIcon from '@mui/icons-material
|
|
25
|
+
import { Close as CloseIcon } from '@mui/icons-material'
|
|
26
26
|
import { WidgetOptions } from '@carto/meridian-ds/custom-icons'
|
|
27
27
|
import { Tooltip } from '../../components'
|
|
28
28
|
import { DEFAULT_TOOLBOX_LABELS, type ToolboxLabels } from './labels'
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
type SxProps,
|
|
19
19
|
type Theme,
|
|
20
20
|
} from '@mui/material'
|
|
21
|
-
import ExpandMoreIcon from '@mui/icons-material
|
|
21
|
+
import { ExpandMore as ExpandMoreIcon } from '@mui/icons-material'
|
|
22
22
|
import { SmartTooltip } from '../../components/smart-tooltip/smart-tooltip'
|
|
23
23
|
import { useWidget, useWidgetId } from '../stores'
|
|
24
24
|
import { Actions, Options } from './widget-actions'
|