@12-apps/ui 6.6.0 → 6.7.0

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 (80) hide show
  1. package/dist/{chunk-3KWD5Y5O.js → chunk-RXE5CUUE.js} +8 -4
  2. package/dist/chunk-RXE5CUUE.js.map +1 -0
  3. package/dist/data-display/Alert.js +1 -1
  4. package/dist/data-display/AsyncStateContainer.js +2 -1
  5. package/dist/data-display/AsyncStateContainer.js.map +1 -1
  6. package/dist/data-display/Banner.js +4 -2
  7. package/dist/data-display/Banner.js.map +1 -1
  8. package/dist/data-display/CardKit.js +3 -1
  9. package/dist/data-display/CardKit.js.map +1 -1
  10. package/dist/data-display/Carousel.js +5 -2
  11. package/dist/data-display/Carousel.js.map +1 -1
  12. package/dist/data-display/DataViews.js +2 -1
  13. package/dist/data-display/DataViews.js.map +1 -1
  14. package/dist/data-display/Lightbox.js +147 -117
  15. package/dist/data-display/Lightbox.js.map +1 -1
  16. package/dist/data-display/MapPreview.js +14 -10
  17. package/dist/data-display/MapPreview.js.map +1 -1
  18. package/dist/pt-BR.js +37 -1
  19. package/dist/pt-BR.js.map +1 -1
  20. package/dist/types/components/data-display/Alert/Alert.d.ts +9 -2
  21. package/dist/types/components/data-display/Alert/Alert.d.ts.map +1 -1
  22. package/dist/types/components/data-display/Alert/Alert.types.d.ts +25 -9
  23. package/dist/types/components/data-display/Alert/Alert.types.d.ts.map +1 -1
  24. package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.d.ts.map +1 -1
  25. package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.types.d.ts +6 -0
  26. package/dist/types/components/data-display/AsyncStateContainer/AsyncStateContainer.types.d.ts.map +1 -1
  27. package/dist/types/components/data-display/Banner/Banner.d.ts.map +1 -1
  28. package/dist/types/components/data-display/Banner/Banner.types.d.ts +2 -0
  29. package/dist/types/components/data-display/Banner/Banner.types.d.ts.map +1 -1
  30. package/dist/types/components/data-display/CardKit/card-actions-context.d.ts +3 -1
  31. package/dist/types/components/data-display/CardKit/card-actions-context.d.ts.map +1 -1
  32. package/dist/types/components/data-display/Carousel/Carousel.chrome.d.ts +3 -0
  33. package/dist/types/components/data-display/Carousel/Carousel.chrome.d.ts.map +1 -1
  34. package/dist/types/components/data-display/Carousel/Carousel.parts.d.ts.map +1 -1
  35. package/dist/types/components/data-display/Carousel/Carousel.types.d.ts +7 -0
  36. package/dist/types/components/data-display/Carousel/Carousel.types.d.ts.map +1 -1
  37. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts +2 -0
  38. package/dist/types/components/data-display/DataViews/data-views-copy.d.ts.map +1 -1
  39. package/dist/types/components/data-display/DataViews/data-views-table-parts.d.ts.map +1 -1
  40. package/dist/types/components/data-display/Lightbox/Lightbox.d.ts.map +1 -1
  41. package/dist/types/components/data-display/Lightbox/Lightbox.types.d.ts +9 -0
  42. package/dist/types/components/data-display/Lightbox/Lightbox.types.d.ts.map +1 -1
  43. package/dist/types/components/data-display/Lightbox/LightboxControls.d.ts.map +1 -1
  44. package/dist/types/components/data-display/Lightbox/lightbox-copy-context.d.ts +17 -0
  45. package/dist/types/components/data-display/Lightbox/lightbox-copy-context.d.ts.map +1 -0
  46. package/dist/types/components/data-display/MapPreview/MapControlBar.d.ts +7 -0
  47. package/dist/types/components/data-display/MapPreview/MapControlBar.d.ts.map +1 -1
  48. package/dist/types/components/data-display/MapPreview/MapLayers.d.ts.map +1 -1
  49. package/dist/types/components/data-display/MapPreview/MapPreview.types.d.ts +9 -0
  50. package/dist/types/components/data-display/MapPreview/MapPreview.types.d.ts.map +1 -1
  51. package/dist/types/components/data-display/MapPreview/MapSearchBar.d.ts +2 -0
  52. package/dist/types/components/data-display/MapPreview/MapSearchBar.d.ts.map +1 -1
  53. package/dist/types/copy.d.ts +53 -0
  54. package/dist/types/copy.d.ts.map +1 -1
  55. package/dist/types/pt-BR.d.ts +5 -1
  56. package/dist/types/pt-BR.d.ts.map +1 -1
  57. package/package.json +1 -1
  58. package/src/components/data-display/Alert/Alert.tsx +22 -5
  59. package/src/components/data-display/Alert/Alert.types.ts +20 -10
  60. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.tsx +2 -1
  61. package/src/components/data-display/AsyncStateContainer/AsyncStateContainer.types.ts +6 -0
  62. package/src/components/data-display/Banner/Banner.tsx +5 -2
  63. package/src/components/data-display/Banner/Banner.types.ts +2 -0
  64. package/src/components/data-display/CardKit/card-actions-context.tsx +4 -0
  65. package/src/components/data-display/Carousel/Carousel.chrome.tsx +5 -0
  66. package/src/components/data-display/Carousel/Carousel.parts.tsx +4 -3
  67. package/src/components/data-display/Carousel/Carousel.types.ts +7 -0
  68. package/src/components/data-display/DataViews/data-views-copy.ts +2 -0
  69. package/src/components/data-display/DataViews/data-views-table-parts.tsx +1 -0
  70. package/src/components/data-display/Lightbox/Lightbox.tsx +3 -0
  71. package/src/components/data-display/Lightbox/Lightbox.types.ts +9 -0
  72. package/src/components/data-display/Lightbox/LightboxControls.tsx +28 -15
  73. package/src/components/data-display/Lightbox/lightbox-copy-context.tsx +42 -0
  74. package/src/components/data-display/MapPreview/MapControlBar.tsx +17 -9
  75. package/src/components/data-display/MapPreview/MapLayers.tsx +2 -0
  76. package/src/components/data-display/MapPreview/MapPreview.types.ts +10 -0
  77. package/src/components/data-display/MapPreview/MapSearchBar.tsx +4 -1
  78. package/src/copy.ts +57 -0
  79. package/src/pt-BR.ts +41 -1
  80. package/dist/chunk-3KWD5Y5O.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { CarouselArrows, CarouselIndicators, CarouselThumbnails } from './Carousel.parts';
4
+ import type { CarouselCopy } from '../../../copy';
4
5
  import type {
5
6
  CarouselArrowsProps,
6
7
  CarouselIndicatorsProps,
@@ -12,6 +13,8 @@ type IndicatorPosition = NonNullable<CarouselIndicatorsProps['position']>;
12
13
  type Color = NonNullable<CarouselIndicatorsProps['color']>;
13
14
 
14
15
  export interface CarouselChromeProps {
16
+ /** The two arrows' accessible names, from the mount. */
17
+ copy: CarouselCopy;
15
18
  items: CarouselItem[];
16
19
  activeIndex: number;
17
20
  loop: boolean;
@@ -34,6 +37,7 @@ export interface CarouselChromeProps {
34
37
  * as three conditional blocks inside the carousel's own render.
35
38
  */
36
39
  export const CarouselChrome: React.FC<CarouselChromeProps> = ({
40
+ copy,
37
41
  items,
38
42
  activeIndex,
39
43
  loop,
@@ -52,6 +56,7 @@ export const CarouselChrome: React.FC<CarouselChromeProps> = ({
52
56
  <>
53
57
  {showArrows && !disabled && (
54
58
  <CarouselArrows
59
+ copy={copy}
55
60
  onPrev={onPrev}
56
61
  onNext={onNext}
57
62
  position={arrowPosition}
@@ -212,7 +212,8 @@ const arrowSx = (theme: Theme, color: Color, isOverlay: boolean): CSSObject => (
212
212
 
213
213
  export const CarouselArrows: React.FC<CarouselArrowsProps> = (props) => {
214
214
  const {
215
- onPrev, onNext, position, disabled, disablePrev, disableNext, color, size, className, style,
215
+ copy, onPrev, onNext, position, disabled, disablePrev, disableNext, color, size, className,
216
+ style,
216
217
  } = withDefaults(props, ARROWS_DEFAULTS) as CarouselArrowsProps &
217
218
  Required<Pick<CarouselArrowsProps, keyof typeof ARROWS_DEFAULTS>>;
218
219
 
@@ -227,7 +228,7 @@ export const CarouselArrows: React.FC<CarouselArrowsProps> = (props) => {
227
228
  disabled={disabled || disablePrev}
228
229
  size={muiSize(size)}
229
230
  className={className}
230
- aria-label="Previous slide"
231
+ aria-label={copy.previous}
231
232
  data-testid="carousel-prev-button"
232
233
  sx={{ ...sx, left: offset, ...style }}
233
234
  >
@@ -237,7 +238,7 @@ export const CarouselArrows: React.FC<CarouselArrowsProps> = (props) => {
237
238
  <IconButton
238
239
  onClick={onNext}
239
240
  disabled={disabled || disableNext}
240
- aria-label="Next slide"
241
+ aria-label={copy.next}
241
242
  size={muiSize(size)}
242
243
  className={className}
243
244
  data-testid="carousel-next-button"
@@ -1,5 +1,6 @@
1
1
  import type { ColorValue, SizeValue } from '../../../tokens/scales';
2
2
  import type { CSSProperties, ReactNode } from 'react';
3
+ import type { CarouselCopy } from '../../../copy';
3
4
 
4
5
  export interface CarouselItem {
5
6
  id: string;
@@ -11,6 +12,11 @@ export interface CarouselItem {
11
12
  }
12
13
 
13
14
  export interface CarouselProps {
15
+ /**
16
+ * The two arrows' accessible names. They render a glyph and nothing else,
17
+ * so this pair IS what a screen reader announces for them. REQUIRED.
18
+ */
19
+ copy: CarouselCopy;
14
20
  items: CarouselItem[];
15
21
  variant?: 'default' | 'glass' | 'gradient' | 'elevated' | 'minimal' | 'cards';
16
22
  size?: SizeValue;
@@ -58,6 +64,7 @@ export interface CarouselIndicatorsProps {
58
64
  }
59
65
 
60
66
  export interface CarouselArrowsProps {
67
+ copy: CarouselCopy;
61
68
  onPrev: () => void;
62
69
  onNext: () => void;
63
70
  position?: 'inside' | 'outside' | 'overlay';
@@ -231,6 +231,8 @@ export interface DataViewsNavCopy {
231
231
  reset: string;
232
232
  /** The inline error when a view could not be refreshed or saved. */
233
233
  updateFailed: string;
234
+ /** The dismiss on that failure's alert — a glyph with no visible label. */
235
+ dismissError: string;
234
236
  saveFailed: string;
235
237
  /** Reopen the save dialog over an existing view. */
236
238
  editView: string;
@@ -36,6 +36,7 @@ export function ViewMutationErrorAlert({
36
36
  title={copy.nav.updateFailed}
37
37
  description={error}
38
38
  closable
39
+ closeLabel={copy.nav.dismissError}
39
40
  onClose={onClose}
40
41
  data-testid={`${testIdPrefix}-views-mutation-error`}
41
42
  />
@@ -3,6 +3,7 @@ import React from 'react';
3
3
 
4
4
  import { makeTestId } from './Lightbox.constants';
5
5
  import { resolveLightboxProps } from './Lightbox.helpers';
6
+ import { LightboxCopyProvider } from './lightbox-copy-context';
6
7
  import type { LightboxProps, LightboxRef } from './Lightbox.types';
7
8
  import { LightboxCurrentMedia } from './LightboxCurrentMedia';
8
9
  import { LightboxOverlay } from './LightboxOverlay';
@@ -32,6 +33,7 @@ export const Lightbox = React.forwardRef<LightboxRef, LightboxProps>((componentP
32
33
  const { currentIndex, currentItem, zoom } = lightbox;
33
34
 
34
35
  return (
36
+ <LightboxCopyProvider copy={props.copy}>
35
37
  <Dialog
36
38
  open={isOpen}
37
39
  onClose={lightbox.close}
@@ -98,6 +100,7 @@ export const Lightbox = React.forwardRef<LightboxRef, LightboxProps>((componentP
98
100
  }
99
101
  />
100
102
  </Dialog>
103
+ </LightboxCopyProvider>
101
104
  );
102
105
  });
103
106
 
@@ -1,4 +1,5 @@
1
1
  import type { CSSProperties } from 'react';
2
+ import type { LightboxCopy } from '../../../copy';
2
3
 
3
4
  export interface LightboxItem {
4
5
  src: string;
@@ -12,6 +13,14 @@ export interface AutoplayConfig {
12
13
  }
13
14
 
14
15
  export interface LightboxProps {
16
+ /**
17
+ * Every word this viewer renders — all of them aria-labels on glyph-only
18
+ * controls, so this object IS what a screen-reader user hears for the whole
19
+ * component. REQUIRED: a default could only be one language read aloud to
20
+ * everybody else's users, and the sighted path looks identical either way,
21
+ * which is why the English literals survived so long.
22
+ */
23
+ copy: LightboxCopy;
15
24
  /** Controls visibility; must lock body scroll when true */
16
25
  isOpen: boolean;
17
26
  /** Fired on close button, backdrop click, Esc, and swipe-down (mobile) */
@@ -11,22 +11,26 @@ import type { FC } from 'react';
11
11
  import React from 'react';
12
12
 
13
13
  import { navButtonSx, OVERLAY_Z_INDEX, overlayButtonSx } from './Lightbox.constants';
14
+ import { useLightboxCopy } from './lightbox-copy-context';
14
15
 
15
16
  type TestId = (suffix: string) => string;
16
17
 
17
18
  export const LightboxCloseButton: FC<{ testId: TestId; onClose: () => void }> = ({
18
19
  testId,
19
20
  onClose,
20
- }) => (
21
+ }) => {
22
+ const copy = useLightboxCopy();
23
+ return (
21
24
  <IconButton
22
25
  onClick={onClose}
23
26
  sx={{ ...overlayButtonSx, position: 'absolute', top: 16, right: 16, zIndex: OVERLAY_Z_INDEX }}
24
- aria-label="Close lightbox"
27
+ aria-label={copy.close}
25
28
  data-testid={testId('close')}
26
29
  >
27
30
  <CloseIcon />
28
31
  </IconButton>
29
- );
32
+ );
33
+ };
30
34
 
31
35
  const arrowSx = (side: 'left' | 'right') =>
32
36
  ({
@@ -52,13 +56,15 @@ export const LightboxNavButtons: FC<LightboxNavButtonsProps> = ({
52
56
  atEnd,
53
57
  onPrev,
54
58
  onNext,
55
- }) => (
59
+ }) => {
60
+ const copy = useLightboxCopy();
61
+ return (
56
62
  <>
57
63
  <IconButton
58
64
  onClick={onPrev}
59
65
  disabled={atStart}
60
66
  sx={arrowSx('left')}
61
- aria-label="Previous item"
67
+ aria-label={copy.previous}
62
68
  data-testid={testId('prev')}
63
69
  >
64
70
  <PrevIcon />
@@ -68,13 +74,14 @@ export const LightboxNavButtons: FC<LightboxNavButtonsProps> = ({
68
74
  onClick={onNext}
69
75
  disabled={atEnd}
70
76
  sx={arrowSx('right')}
71
- aria-label="Next item"
77
+ aria-label={copy.next}
72
78
  data-testid={testId('next')}
73
79
  >
74
80
  <NextIcon />
75
81
  </IconButton>
76
82
  </>
77
- );
83
+ );
84
+ };
78
85
 
79
86
  export interface LightboxZoomControlsProps {
80
87
  testId: TestId;
@@ -88,7 +95,9 @@ export const LightboxZoomControls: FC<LightboxZoomControlsProps> = ({
88
95
  onZoomIn,
89
96
  onZoomOut,
90
97
  onResetZoom,
91
- }) => (
98
+ }) => {
99
+ const copy = useLightboxCopy();
100
+ return (
92
101
  <Box
93
102
  sx={{
94
103
  position: 'absolute',
@@ -101,13 +110,13 @@ export const LightboxZoomControls: FC<LightboxZoomControlsProps> = ({
101
110
  }}
102
111
  data-testid={testId('zoom-controls')}
103
112
  >
104
- <IconButton onClick={onZoomIn} sx={overlayButtonSx} aria-label="Zoom in" data-testid={testId('zoom-in')}>
113
+ <IconButton onClick={onZoomIn} sx={overlayButtonSx} aria-label={copy.zoomIn} data-testid={testId('zoom-in')}>
105
114
  <ZoomInIcon />
106
115
  </IconButton>
107
116
  <IconButton
108
117
  onClick={onZoomOut}
109
118
  sx={overlayButtonSx}
110
- aria-label="Zoom out"
119
+ aria-label={copy.zoomOut}
111
120
  data-testid={testId('zoom-out')}
112
121
  >
113
122
  <ZoomOutIcon />
@@ -115,13 +124,14 @@ export const LightboxZoomControls: FC<LightboxZoomControlsProps> = ({
115
124
  <IconButton
116
125
  onClick={onResetZoom}
117
126
  sx={overlayButtonSx}
118
- aria-label="Reset zoom"
127
+ aria-label={copy.resetZoom}
119
128
  data-testid={testId('zoom-reset')}
120
129
  >
121
130
  <ResetZoomIcon />
122
131
  </IconButton>
123
132
  </Box>
124
- );
133
+ );
134
+ };
125
135
 
126
136
  export interface LightboxAutoplayButtonProps {
127
137
  testId: TestId;
@@ -133,7 +143,9 @@ export const LightboxAutoplayButton: FC<LightboxAutoplayButtonProps> = ({
133
143
  testId,
134
144
  isAutoPlaying,
135
145
  onToggle,
136
- }) => (
146
+ }) => {
147
+ const copy = useLightboxCopy();
148
+ return (
137
149
  <IconButton
138
150
  onClick={onToggle}
139
151
  sx={{
@@ -144,9 +156,10 @@ export const LightboxAutoplayButton: FC<LightboxAutoplayButtonProps> = ({
144
156
  transform: 'translateX(-50%)',
145
157
  zIndex: OVERLAY_Z_INDEX,
146
158
  }}
147
- aria-label={isAutoPlaying ? 'Pause slideshow' : 'Start slideshow'}
159
+ aria-label={isAutoPlaying ? copy.pause : copy.play}
148
160
  data-testid={testId('autoplay')}
149
161
  >
150
162
  {isAutoPlaying ? <PauseIcon /> : <PlayIcon />}
151
163
  </IconButton>
152
- );
164
+ );
165
+ };
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { createContext, useContext, type ReactNode } from 'react';
4
+ import React from 'react';
5
+
6
+ import type { LightboxCopy } from '../../../copy';
7
+
8
+ /**
9
+ * The lightbox's words, for the four control components that render them.
10
+ *
11
+ * A CONTEXT rather than props: the viewer is `Lightbox` → `LightboxOverlay` →
12
+ * three separate control clusters, and threading one object down three hops is
13
+ * how a copy port comes to exist, be required, and go unread. It stays a
14
+ * single REQUIRED prop at the mount, which is the only place a host answers.
15
+ */
16
+ const LightboxCopyContext = createContext<LightboxCopy | null>(null);
17
+
18
+ export function LightboxCopyProvider({
19
+ copy,
20
+ children,
21
+ }: {
22
+ copy: LightboxCopy;
23
+ children: ReactNode;
24
+ }): React.JSX.Element {
25
+ return <LightboxCopyContext.Provider value={copy}>{children}</LightboxCopyContext.Provider>;
26
+ }
27
+
28
+ /**
29
+ * The words this viewer renders — THROWS outside a provider rather than
30
+ * falling back.
31
+ *
32
+ * Every one of them is an aria-label on a glyph, so a fallback would be the
33
+ * one failure nobody sees in review: the sighted path renders identically
34
+ * whichever language it is in.
35
+ */
36
+ export function useLightboxCopy(): LightboxCopy {
37
+ const copy = useContext(LightboxCopyContext);
38
+ if (!copy) {
39
+ throw new Error('useLightboxCopy must be rendered inside a <LightboxCopyProvider>');
40
+ }
41
+ return copy;
42
+ }
@@ -9,6 +9,7 @@ import React from 'react';
9
9
 
10
10
  import { MAX_ZOOM, MIN_ZOOM } from './MapPreview.constants';
11
11
  import type { MapType } from './MapPreview.types';
12
+ import type { MapPreviewCopy } from '../../../copy';
12
13
 
13
14
  // Kept module-local: the inferred type of a styled() component cannot be named
14
15
  // across a module boundary here (TS2742).
@@ -41,6 +42,12 @@ const ControlButton = styled(IconButton)(({ theme }) => ({
41
42
  }));
42
43
 
43
44
  export interface MapControlBarProps {
45
+ /**
46
+ * The six controls' names. Every one is a glyph with a `title` AND an
47
+ * `aria-label`, so this object is both the tooltip and what a screen reader
48
+ * reads — REQUIRED, because a default is one language for every adopter.
49
+ */
50
+ copy: MapPreviewCopy;
44
51
  zoom: number;
45
52
  mapType: MapType;
46
53
  onZoomIn: () => void;
@@ -51,6 +58,7 @@ export interface MapControlBarProps {
51
58
  }
52
59
 
53
60
  export const MapControlBar: FC<MapControlBarProps> = ({
61
+ copy,
54
62
  zoom,
55
63
  mapType,
56
64
  onZoomIn,
@@ -60,33 +68,33 @@ export const MapControlBar: FC<MapControlBarProps> = ({
60
68
  onToggleFullscreen,
61
69
  }) => (
62
70
  <MapControls data-testid="map-controls">
63
- <Tooltip title="Zoom in" placement="left">
71
+ <Tooltip title={copy.zoomIn} placement="left">
64
72
  <ControlButton
65
73
  size="small"
66
74
  onClick={onZoomIn}
67
- aria-label="Zoom in"
75
+ aria-label={copy.zoomIn}
68
76
  data-testid="zoom-in-button"
69
77
  disabled={zoom >= MAX_ZOOM}
70
78
  >
71
79
  <ZoomInIcon fontSize="small" />
72
80
  </ControlButton>
73
81
  </Tooltip>
74
- <Tooltip title="Zoom out" placement="left">
82
+ <Tooltip title={copy.zoomOut} placement="left">
75
83
  <ControlButton
76
84
  size="small"
77
85
  onClick={onZoomOut}
78
- aria-label="Zoom out"
86
+ aria-label={copy.zoomOut}
79
87
  data-testid="zoom-out-button"
80
88
  disabled={zoom <= MIN_ZOOM}
81
89
  >
82
90
  <ZoomOutIcon fontSize="small" />
83
91
  </ControlButton>
84
92
  </Tooltip>
85
- <Tooltip title="Center map" placement="left">
93
+ <Tooltip title={copy.center} placement="left">
86
94
  <ControlButton
87
95
  size="small"
88
96
  onClick={onCenter}
89
- aria-label="Center map"
97
+ aria-label={copy.center}
90
98
  data-testid="center-button"
91
99
  >
92
100
  <CenterIcon fontSize="small" />
@@ -96,17 +104,17 @@ export const MapControlBar: FC<MapControlBarProps> = ({
96
104
  <ControlButton
97
105
  size="small"
98
106
  onClick={onToggleMapType}
99
- aria-label="Change map type"
107
+ aria-label={copy.mapType}
100
108
  data-testid="map-type-button"
101
109
  >
102
110
  <LayersIcon fontSize="small" />
103
111
  </ControlButton>
104
112
  </Tooltip>
105
- <Tooltip title="Toggle fullscreen" placement="left">
113
+ <Tooltip title={copy.fullscreen} placement="left">
106
114
  <ControlButton
107
115
  size="small"
108
116
  onClick={onToggleFullscreen}
109
- aria-label="Toggle fullscreen"
117
+ aria-label={copy.fullscreen}
110
118
  data-testid="fullscreen-button"
111
119
  >
112
120
  <FullscreenIcon fontSize="small" />
@@ -46,6 +46,7 @@ export const MapLayers: FC<MapLayersProps> = ({ settings, zoom, mapType, search,
46
46
 
47
47
  {settings.showSearch && (
48
48
  <MapSearchBar
49
+ label={settings.copy.search}
49
50
  value={search.searchValue}
50
51
  placeholder={settings.searchPlaceholder}
51
52
  isSearching={search.isSearching}
@@ -56,6 +57,7 @@ export const MapLayers: FC<MapLayersProps> = ({ settings, zoom, mapType, search,
56
57
 
57
58
  {settings.showControls && (
58
59
  <MapControlBar
60
+ copy={settings.copy}
59
61
  zoom={zoom}
60
62
  mapType={mapType}
61
63
  onZoomIn={controls.handleZoomIn}
@@ -1,3 +1,5 @@
1
+ import type { MapPreviewCopy } from '../../../copy';
2
+
1
3
  export type MapType = 'roadmap' | 'satellite' | 'hybrid' | 'terrain';
2
4
 
3
5
  export interface LatLng {
@@ -20,6 +22,14 @@ export interface HeatmapPoint {
20
22
  }
21
23
 
22
24
  export interface MapPreviewProps {
25
+ /**
26
+ * The control bar's six names and the search field's own. Every control is a
27
+ * glyph carrying a `title` and an `aria-label`, so this is both the tooltip
28
+ * and what a screen reader reads. REQUIRED — `searchPlaceholder` beside it
29
+ * shows the shape this replaces: an English default that read as
30
+ * configurable right up until nobody configured it.
31
+ */
32
+ copy: MapPreviewCopy;
23
33
  /** Map center coordinates */
24
34
  center?: { lat: number; lng: number };
25
35
  /** Legacy coordinates prop (use center instead) */
@@ -28,6 +28,8 @@ const SearchField = styled(TextField)(({ theme }) => ({
28
28
  }));
29
29
 
30
30
  export interface MapSearchBarProps {
31
+ /** The field's accessible name — it renders no visible label. REQUIRED. */
32
+ label: string;
31
33
  value: string;
32
34
  placeholder: string;
33
35
  isSearching: boolean;
@@ -36,6 +38,7 @@ export interface MapSearchBarProps {
36
38
  }
37
39
 
38
40
  export const MapSearchBar: FC<MapSearchBarProps> = ({
41
+ label,
39
42
  value,
40
43
  placeholder,
41
44
  isSearching,
@@ -58,7 +61,7 @@ export const MapSearchBar: FC<MapSearchBarProps> = ({
58
61
  ),
59
62
  }}
60
63
  disabled={isSearching}
61
- aria-label="Search location"
64
+ aria-label={label}
62
65
  />
63
66
  </SearchBar>
64
67
  );
package/src/copy.ts CHANGED
@@ -129,4 +129,61 @@ export interface CepFieldCopy {
129
129
  export interface TableFilterCopy {
130
130
  /** A numeric range whose maximum is below its minimum. */
131
131
  invalidRange: string;
132
+ /** The two free-text bounds, which carry no visible label of their own. */
133
+ rangeMin: string;
134
+ rangeMax: string;
135
+ }
136
+
137
+ /**
138
+ * The lightbox's controls, every one of them a glyph with no text.
139
+ *
140
+ * So this object IS what a screen-reader user hears for the whole viewer —
141
+ * which is why it shipped as English literals for so long without anyone
142
+ * noticing: the sighted path renders identically either way.
143
+ */
144
+ export interface LightboxCopy {
145
+ close: string;
146
+ previous: string;
147
+ next: string;
148
+ zoomIn: string;
149
+ zoomOut: string;
150
+ resetZoom: string;
151
+ /** The slideshow toggle, in its two states. */
152
+ play: string;
153
+ pause: string;
154
+ }
155
+
156
+ /** The map preview's control bar — same shape, same reason, as the lightbox. */
157
+ export interface MapPreviewCopy {
158
+ zoomIn: string;
159
+ zoomOut: string;
160
+ center: string;
161
+ mapType: string;
162
+ fullscreen: string;
163
+ search: string;
164
+ }
165
+
166
+ /** The carousel's two arrows, which carry a glyph and nothing else. */
167
+ export interface CarouselCopy {
168
+ previous: string;
169
+ next: string;
170
+ }
171
+
172
+ /**
173
+ * The states a data surface can be in with nothing to draw, and the dismiss
174
+ * every banner-like surface carries.
175
+ *
176
+ * One object rather than six, because these are the same four sentences the
177
+ * table, the grid, the async container, the hover card and the infinite
178
+ * scroller each rendered their own copy of.
179
+ */
180
+ export interface DataStateCopy {
181
+ /** No rows at all — distinct from "no rows for these filters". */
182
+ empty: string;
183
+ loading: string;
184
+ /** The infinite scroller reached the end. */
185
+ endOfList: string;
186
+ /** An alert's and a banner's dismiss, neither of which has a visible label. */
187
+ dismissAlert: string;
188
+ dismissBanner: string;
132
189
  }
package/src/pt-BR.ts CHANGED
@@ -1,12 +1,16 @@
1
1
  import type { DataViewsCopy } from "./components/data-display/DataViews/data-views-copy";
2
2
  import type { SavedViewsLabels } from "./components/layout/ContentToolbar/ContentToolbar.types";
3
3
  import type {
4
+ CarouselCopy,
4
5
  CategorySelectCopy,
5
6
  CepFieldCopy,
6
7
  ConfirmActionCopy,
8
+ DataStateCopy,
9
+ LightboxCopy,
10
+ MapPreviewCopy,
7
11
  SectionOnboardingCopy,
8
12
  TableFilterCopy,
9
- } from "./copy";
13
+ } from './copy';
10
14
 
11
15
  /**
12
16
  * The pt-BR packs — NAMED constants a host passes by hand, never defaults.
@@ -101,6 +105,41 @@ export const PT_BR_SAVED_VIEWS_LABELS: SavedViewsLabels = {
101
105
 
102
106
  export const PT_BR_TABLE_FILTER_COPY: TableFilterCopy = {
103
107
  invalidRange: "O máximo deve ser maior ou igual ao mínimo.",
108
+ rangeMin: "Mínimo",
109
+ rangeMax: "Máximo",
110
+ };
111
+
112
+ export const PT_BR_LIGHTBOX_COPY: LightboxCopy = {
113
+ close: "Fechar",
114
+ previous: "Anterior",
115
+ next: "Próximo",
116
+ zoomIn: "Aproximar",
117
+ zoomOut: "Afastar",
118
+ resetZoom: "Restaurar o zoom",
119
+ play: "Iniciar a apresentação",
120
+ pause: "Pausar a apresentação",
121
+ };
122
+
123
+ export const PT_BR_MAP_PREVIEW_COPY: MapPreviewCopy = {
124
+ zoomIn: "Aproximar",
125
+ zoomOut: "Afastar",
126
+ center: "Centralizar o mapa",
127
+ mapType: "Trocar o tipo de mapa",
128
+ fullscreen: "Tela cheia",
129
+ search: "Buscar endereço",
130
+ };
131
+
132
+ export const PT_BR_CAROUSEL_COPY: CarouselCopy = {
133
+ previous: "Anterior",
134
+ next: "Próximo",
135
+ };
136
+
137
+ export const PT_BR_DATA_STATE_COPY: DataStateCopy = {
138
+ empty: "Nenhum dado por aqui",
139
+ loading: "Carregando…",
140
+ endOfList: "Não há mais itens para carregar",
141
+ dismissAlert: "Fechar o aviso",
142
+ dismissBanner: "Fechar a faixa",
104
143
  };
105
144
 
106
145
  export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
@@ -260,6 +299,7 @@ export const PT_BR_DATA_VIEWS_COPY: DataViewsCopy = {
260
299
  saveAs: "Salvar como nova",
261
300
  reset: "Redefinir",
262
301
  updateFailed: "Não foi possível atualizar a visão",
302
+ dismissError: "Fechar o aviso",
263
303
  saveFailed: "Não foi possível salvar a alteração da visão.",
264
304
  editView: "Renomear e editar",
265
305
  pinToSidebar: "Fixar na barra lateral",