From 28162b83005ac1510c6eb9c0dd82a444c3c16444 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 12 Nov 2026 18:26:25 +0100 Subject: [PATCH 01/11] fix: Keyboard access to menus, confirmation dialogs and the add chart popover --- app/components/action-menu.spec.tsx | 118 +++++++++++++ app/components/action-menu.tsx | 60 +++++++ app/components/arrow-menu.tsx | 18 +- app/components/chart-selection-tabs.tsx | 42 +++-- app/components/chart-shared.spec.tsx | 1 + app/components/chart-shared.tsx | 3 + app/components/confirmation-dialog.tsx | 21 ++- app/components/dashboard-shared.tsx | 72 ++++---- app/components/menu-action-item.tsx | 210 +++++++++++++----------- app/components/rename-dialog.tsx | 2 +- app/components/row-actions.tsx | 20 ++- app/login/components/login-menu.tsx | 11 +- app/login/components/profile-tables.tsx | 2 +- app/utils/focus-first.ts | 3 + 14 files changed, 415 insertions(+), 168 deletions(-) create mode 100644 app/components/action-menu.spec.tsx create mode 100644 app/components/action-menu.tsx create mode 100644 app/utils/focus-first.ts diff --git a/app/components/action-menu.spec.tsx b/app/components/action-menu.spec.tsx new file mode 100644 index 0000000000..5add9d088f --- /dev/null +++ b/app/components/action-menu.spec.tsx @@ -0,0 +1,118 @@ +import { + cleanup, + fireEvent, + render, + screen, + waitFor, +} from "@testing-library/react"; +import { ReactNode, useState } from "react"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import { ActionMenu } from "@/components/action-menu"; +import { MenuActionItem } from "@/components/menu-action-item"; + +vi.mock("@lingui/macro", () => ({ + Trans: ({ children }: { children: ReactNode }) => children, + t: ({ message }: { message: string }) => message, +})); + +afterEach(cleanup); + +const WrappedItem = ({ label }: { label: string }) => { + return ( + {}} + /> + ); +}; + +const TestMenu = ({ + onDelete, + onParentClick, +}: { + onDelete: () => void; + onParentClick: () => void; +}) => { + const [anchor, setAnchor] = useState(null); + + return ( +
+ + setAnchor(null)} + transitionDuration={0} + > + + + + +
+ ); +}; + +const renderMenu = () => { + const onDelete = vi.fn(); + const onParentClick = vi.fn(); + render(); + fireEvent.click(screen.getByRole("button", { name: "Open" })); + onParentClick.mockClear(); + + return { onDelete, onParentClick }; +}; + +describe("ActionMenu", () => { + it("focuses the first item even when items are wrapped in components", () => { + renderMenu(); + + expect(document.activeElement).toBe( + screen.getByRole("menuitem", { name: "Duplicate" }) + ); + }); + + it("moves focus between items with the arrow keys", () => { + renderMenu(); + fireEvent.keyDown(document.activeElement!, { key: "ArrowDown" }); + + expect(document.activeElement).toBe( + screen.getByRole("menuitem", { name: "Export" }) + ); + }); + + it("renders the confirmation outside the closed menu", async () => { + const { onDelete, onParentClick } = renderMenu(); + fireEvent.click(screen.getByRole("menuitem", { name: "Delete" })); + + expect(screen.getByRole("dialog", { name: "Delete chart?" })).toBeTruthy(); + expect(onParentClick).not.toHaveBeenCalled(); + await waitFor(() => { + expect(screen.queryByRole("menu", { hidden: true })).toBeNull(); + }); + + fireEvent.click(screen.getByRole("button", { name: "Yes" })); + + expect(onDelete).toHaveBeenCalledTimes(1); + }); + + it("keeps the menu closed when the confirmation is cancelled", async () => { + const { onDelete } = renderMenu(); + fireEvent.click(screen.getByRole("menuitem", { name: "Delete" })); + fireEvent.click(screen.getByRole("button", { name: "No" })); + + expect(onDelete).not.toHaveBeenCalled(); + await waitFor(() => { + expect(screen.queryByRole("menu", { hidden: true })).toBeNull(); + }); + }); +}); diff --git a/app/components/action-menu.tsx b/app/components/action-menu.tsx new file mode 100644 index 0000000000..134221a92f --- /dev/null +++ b/app/components/action-menu.tsx @@ -0,0 +1,60 @@ +import { Menu, MenuProps } from "@mui/material"; +import { createContext, MouseEvent, useContext, useState } from "react"; + +import { + ConfirmationDialog, + ConfirmationRequest, +} from "@/components/confirmation-dialog"; +import { focusFirst } from "@/utils/focus-first"; + +const MENU_ITEM_SELECTOR = '[role^="menuitem"]:not([aria-disabled="true"])'; + +const RequestConfirmationContext = createContext< + ((request: ConfirmationRequest) => void) | undefined +>(undefined); + +export const useRequestConfirmation = () => { + return useContext(RequestConfirmationContext); +}; + +export const ActionMenu = ({ + open, + onClose, + TransitionProps, + ...props +}: MenuProps) => { + const [request, setRequest] = useState(); + + return ( + + { + focusFirst(node, MENU_ITEM_SELECTOR); + TransitionProps?.onEntering?.(node, isAppearing); + }, + }} + {...props} + /> + {request ? ( + { + setRequest(undefined); + onClose?.(event, reason); + }} + /> + ) : null} + + ); +}; + +const stopPropagation = (e: MouseEvent) => { + e.stopPropagation(); +}; diff --git a/app/components/arrow-menu.tsx b/app/components/arrow-menu.tsx index da2151d4f6..594a741997 100644 --- a/app/components/arrow-menu.tsx +++ b/app/components/arrow-menu.tsx @@ -1,4 +1,6 @@ -import { Menu, paperClasses, styled } from "@mui/material"; +import { paperClasses, Popover, styled } from "@mui/material"; + +import { ActionMenu } from "@/components/action-menu"; const commonBeforeStyles: Partial = { content: '" "', @@ -11,7 +13,7 @@ const commonBeforeStyles: Partial = { transform: "rotate(45deg)", }; -export const ArrowMenuTopCenter = styled(Menu)({ +const topCenterStyles = { [`& .${paperClasses.root}`]: { overflow: "visible", "&:before": { @@ -21,9 +23,13 @@ export const ArrowMenuTopCenter = styled(Menu)({ right: 0, }, }, -}); +}; + +export const ArrowMenuTopCenter = styled(ActionMenu)(topCenterStyles); + +export const ArrowPopoverTopCenter = styled(Popover)(topCenterStyles); -export const ArrowMenuBottomCenter = styled(Menu)({ +export const ArrowMenuBottomCenter = styled(ActionMenu)({ [`& .${paperClasses.root}`]: { overflow: "visible", "&:before": { @@ -35,7 +41,7 @@ export const ArrowMenuBottomCenter = styled(Menu)({ }, }); -export const ArrowMenuTopBottom = styled(Menu)({ +export const ArrowMenuTopBottom = styled(ActionMenu)({ [`& .${paperClasses.root}`]: { overflow: "visible", "&:before": { @@ -47,7 +53,7 @@ export const ArrowMenuTopBottom = styled(Menu)({ }, }); -export const ArrowMenuBottomTop = styled(Menu)({ +export const ArrowMenuBottomTop = styled(ActionMenu)({ [`& .${paperClasses.root}`]: { overflow: "visible", "&:before": { diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index 6a95931446..f205a05d2b 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -23,7 +23,10 @@ import { import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd"; import { AddButton } from "@/components/add-button"; -import { ArrowMenuTopCenter } from "@/components/arrow-menu"; +import { + ArrowMenuTopCenter, + ArrowPopoverTopCenter, +} from "@/components/arrow-menu"; import { DuplicateChartMenuActionItem } from "@/components/chart-shared"; import { DragHandle } from "@/components/drag-handle"; import { Flex } from "@/components/flex"; @@ -44,9 +47,11 @@ import { } from "@/configurator"; import { useSearchDatasetPanelStore } from "@/configurator/components/add-new-dataset-panel"; import { ChartTypeSelector } from "@/configurator/components/chart-type-selector"; +import { getFieldLabel } from "@/configurator/components/field-i18n"; import { getIconName } from "@/configurator/components/ui-helpers"; -import { Icon, IconName } from "@/icons"; +import { Icon } from "@/icons"; import { useLocale } from "@/locales"; +import { focusFirst } from "@/utils/focus-first"; import { useEvent } from "@/utils/use-event"; type TabsState = { @@ -186,7 +191,7 @@ const TabsEditable = (props: TabsEditableProps) => { /> {tabsState.popoverType === "add" ? ( - { vertical: "top", }} onClose={handleClose} + TransitionProps={{ + onEntering: (node) => focusFirst(node, "button:not(:disabled)"), + }} PaperProps={{ + role: "dialog", + "aria-label": t({ + id: "chart-selection-tabs.add-chart", + message: "Add chart", + }), sx: { width: "calc(320px + 2rem)", mt: 3, py: "1rem", - "& > .MuiList-root": { - py: 0, - }, + boxShadow: 4, }, }} > @@ -250,7 +261,7 @@ const TabsEditable = (props: TabsEditableProps) => { - + ) : null} {tabsState.popoverType === "edit" ? ( @@ -496,7 +507,7 @@ const TabsInner = ({ }} label={ void; }) => { const { - iconName, + chartType, chartKey, editable, draggable, @@ -639,6 +650,9 @@ const TabContent = (props: { - ); - } + ) : ( + + )} + {confirmation && !requestConfirmation ? ( + + ) : null} + + ); +}; + +const ActionMenuItem = ({ + label, + leadingIconName, + trailingIconName, + color, + disabled, + linkProps, + onClick, +}: Pick< + MenuActionProps, + "label" | "leadingIconName" | "trailingIconName" | "color" | "disabled" +> & { + linkProps?: { href: string; target?: string; rel?: string }; + onClick: (e: MouseEvent) => void; +}) => { + const content = ( + <> + {leadingIconName && } + {label} + {trailingIconName && ( + + )} + + ); - return ( - <> + return ( + <> + {linkProps ? ( - {leadingIcon && } - {label} - {trailingIcon && ( - - )} + {content} + + ) : ( + + {content} - - - ); - }; - - return ( - <> - {props.type === "link" ? ( - - - - ) : props.type === "button" ? ( - - ) : null} - {props.type === "button" && props.requireConfirmation && ( - )} + ); }; + +const MenuItemLink = forwardRef>( + (props, ref) => +); diff --git a/app/components/rename-dialog.tsx b/app/components/rename-dialog.tsx index c73389b1d8..caf7036ba1 100644 --- a/app/components/rename-dialog.tsx +++ b/app/components/rename-dialog.tsx @@ -87,7 +87,7 @@ export const RenameDialog = ({ : config.data.layout.meta; return ( - +
diff --git a/app/components/row-actions.tsx b/app/components/row-actions.tsx index 0b997dbd50..490d1ab628 100644 --- a/app/components/row-actions.tsx +++ b/app/components/row-actions.tsx @@ -1,3 +1,4 @@ +import { t } from "@lingui/macro"; import { IconButton } from "@mui/material"; import { Flex } from "@/components/flex"; @@ -5,7 +6,13 @@ import { MenuActionItem, MenuActionProps } from "@/components/menu-action-item"; import { useFlipMenu } from "@/components/use-flip-menu"; import { Icon } from "@/icons"; -export const RowActions = ({ actions }: { actions: MenuActionProps[] }) => { +export const RowActions = ({ + actions, + label, +}: { + actions: MenuActionProps[]; + label: string; +}) => { const [primaryAction, ...otherActions] = actions; const { buttonRef, @@ -22,7 +29,16 @@ export const RowActions = ({ actions }: { actions: MenuActionProps[] }) => { return ( - + { {user.name} - { await signOut({ callbackUrl: "/api/auth/signout" }) } /> - + -
+ ); }; diff --git a/app/login/components/profile-tables.tsx b/app/login/components/profile-tables.tsx index adb900953f..95c6093469 100644 --- a/app/login/components/profile-tables.tsx +++ b/app/login/components/profile-tables.tsx @@ -433,7 +433,7 @@ const ProfileVisualizationsRow = ({ })} - + { + node.querySelector(selector)?.focus(); +}; From 541165170db7b55c93aba0bea5df8065c4e92a5f Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Thu, 12 Nov 2026 18:26:25 +0100 Subject: [PATCH 02/11] fix: Keyboard operable time sliders and time range brush --- app/charts/shared/brush/index.tsx | 162 ++++++++++-------- .../dashboard-interactive-filters.tsx | 62 ++++--- .../interactive-filters/editor-brush.tsx | 2 +- .../interactive-filters/time-slider.tsx | 36 ++-- app/utils/observables.spec.ts | 16 ++ app/utils/observables.ts | 25 +++ e2e/time-slider.spec.ts | 24 +++ 7 files changed, 221 insertions(+), 106 deletions(-) diff --git a/app/charts/shared/brush/index.tsx b/app/charts/shared/brush/index.tsx index a55b5a5199..2acc8f35fc 100644 --- a/app/charts/shared/brush/index.tsx +++ b/app/charts/shared/brush/index.tsx @@ -1,5 +1,5 @@ +import { t } from "@lingui/macro"; import { useEventCallback } from "@mui/material"; -import { bisector } from "d3-array"; import { BrushSelection, brushX } from "d3-brush"; import { pointer, pointers, select, Selection } from "d3-selection"; import { Transition } from "d3-transition"; @@ -17,11 +17,7 @@ import type { import { useChartState } from "@/charts/shared/chart-state"; import { useChartTheme } from "@/charts/shared/use-chart-theme"; import { DashboardTimeRangeFilter } from "@/configurator"; -import { - Observation, - TemporalDimension, - TemporalEntityDimension, -} from "@/domain/data"; +import { TemporalDimension, TemporalEntityDimension } from "@/domain/data"; import { useTimeFormatUnit } from "@/formatters"; import { useChartInteractiveFilters, @@ -221,67 +217,36 @@ export const BrushTime = ({ yOffset }: { yOffset?: number }) => { } }); - /** Keyboard support */ - const moveBrushOnKeyPress = useCallback( - (event: $FixMe, handleDirection: "w" | "e") => { - if (from && to) { - updateBrushEndedStatus(false); - - const bisectDateLeft = bisector( - (ds: Observation, date: Date) => - getDate(ds).getTime() - date.getTime() - ).left; - const bisectDateRight = bisector( - (ds: Observation, date: Date) => - getDate(ds).getTime() - date.getTime() - ).right; - - if (event.keyCode === 37 && handleDirection === "w") { - // west handle, moving left - const index = bisectDateLeft(fullData, from, 1); - const indexLeft = fullData[index - 1]; - - if (getDate(indexLeft).getTime() < to.getTime()) { - // new lower than "to" - setTimeRange(getDate(indexLeft), to); - } else { - // new too high, don't do anything - setTimeRange(from, to); - } - } else if (event.keyCode === 39 && handleDirection === "w") { - // west handle, moving right - const index = bisectDateRight(fullData, from, 1); - const indexRight = fullData[index]; - if (getDate(indexRight).getTime() < to.getTime()) { - setTimeRange(getDate(indexRight), to); - } else { - setTimeRange(from, to); - } - } else if (event.keyCode === 37 && handleDirection === "e") { - // east handle, moving left - const index = bisectDateLeft(fullData, to, 1); - const indexLeft = fullData[index - 1]; - - if (getDate(indexLeft).getTime() > from.getTime()) { - setTimeRange(from, getDate(indexLeft)); - } else { - setTimeRange(from, to); - } - } else if (event.keyCode === 39 && handleDirection === "e") { - // east handle, moving right - const index = bisectDateRight(fullData, to, 1); - const indexLeft = fullData[index]; - - if (indexLeft && getDate(indexLeft).getTime() > from.getTime()) { - setTimeRange(from, getDate(indexLeft)); - } else { - setTimeRange(from, to); - } - } - updateBrushEndedStatus(true); + const dates = useMemo(() => { + return Array.from(new Set(fullData.map((d) => getDate(d).getTime()))) + .sort((a, b) => a - b) + .map((d) => new Date(d)); + }, [fullData, getDate]); + + const moveBrushOnKeyDown = useEventCallback( + (event: KeyboardEvent, handle: "w" | "e") => { + const fromIndex = getDateIndex(dates, closestFrom); + const toIndex = getDateIndex(dates, closestTo); + const [min, max, index] = + handle === "w" + ? [0, toIndex - 1, fromIndex] + : [fromIndex + 1, dates.length - 1, toIndex]; + const newIndex = getKeyboardIndex({ key: event.key, index, min, max }); + + if (newIndex === undefined || !ref.current) { + return; } - }, - [fullData, setTimeRange, from, getDate, to] + + event.preventDefault(); + const [newFrom, newTo] = + handle === "w" + ? [dates[newIndex], closestTo] + : [closestFrom, dates[newIndex]]; + select(ref.current).call(brush.move, [ + brushWidthScale(newFrom), + brushWidthScale(newTo), + ]); + } ); useEffect(() => { @@ -329,10 +294,12 @@ export const BrushTime = ({ yOffset }: { yOffset?: number }) => { g.select(".handle--w") .attr("tabindex", 0) - .on("keydown", (e: $FixMe) => moveBrushOnKeyPress(e, "w")); + .attr("role", "slider") + .on("keydown", (e: KeyboardEvent) => moveBrushOnKeyDown(e, "w")); g.select(".handle--e") .attr("tabindex", 0) - .on("keydown", (e: $FixMe) => moveBrushOnKeyPress(e, "e")); + .attr("role", "slider") + .on("keydown", (e: KeyboardEvent) => moveBrushOnKeyDown(e, "e")); }; g.call(mkBrush); @@ -344,10 +311,38 @@ export const BrushTime = ({ yOffset }: { yOffset?: number }) => { brushHandleStrokeColor, brushOverlayColor, brushSelectionColor, - moveBrushOnKeyPress, + moveBrushOnKeyDown, selectionExtent, ]); + useEffect(() => { + if (!ref.current || !closestFrom || !closestTo) { + return; + } + + const g = select(ref.current); + const handles = [ + { + selector: ".handle--w", + label: t({ id: "controls.filters.select.from", message: "From" }), + date: closestFrom, + }, + { + selector: ".handle--e", + label: t({ id: "controls.filters.select.to", message: "To" }), + date: closestTo, + }, + ]; + handles.forEach(({ selector, label, date }) => { + g.select(selector) + .attr("aria-label", label) + .attr("aria-valuemin", 0) + .attr("aria-valuemax", dates.length - 1) + .attr("aria-valuenow", getDateIndex(dates, date)) + .attr("aria-valuetext", formatDate(date)); + }); + }); + // This effect allows "snapping" to actual data points // after brush is ended and interactive-filters state is updated const closestFromStr = closestFrom?.toString(); // Local variables to prevent eslint-plugin-react-hooks bug @@ -433,6 +428,35 @@ export const BrushTime = ({ yOffset }: { yOffset?: number }) => { ) : null; }; +const getDateIndex = (dates: Date[], date: Date) => { + return dates.findIndex((d) => d.getTime() === date.getTime()); +}; + +const getKeyboardIndex = ({ + key, + index, + min, + max, +}: { + key: string; + index: number; + min: number; + max: number; +}) => { + switch (key) { + case "ArrowLeft": + case "ArrowDown": + return Math.max(min, index - 1); + case "ArrowRight": + case "ArrowUp": + return Math.min(max, index + 1); + case "Home": + return min; + case "End": + return max; + } +}; + // Unused BrushOrdinal // export const BrushOrdinal = ({ debug = false }: { debug?: boolean }) => { diff --git a/app/components/dashboard-interactive-filters.tsx b/app/components/dashboard-interactive-filters.tsx index 30b1141e6d..e5a8ff7abf 100644 --- a/app/components/dashboard-interactive-filters.tsx +++ b/app/components/dashboard-interactive-filters.tsx @@ -1,3 +1,4 @@ +import { t } from "@lingui/macro"; import { Box, BoxProps, @@ -8,6 +9,7 @@ import { } from "@mui/material"; import { Theme } from "@mui/material/styles"; import { makeStyles } from "@mui/styles"; +import { bisectLeft } from "d3-array"; import uniq from "lodash/uniq"; import { ChangeEvent, useEffect, useMemo, useRef, useState } from "react"; @@ -213,43 +215,57 @@ const DashboardTimeRangeSlider = ({ const mountedForSomeTime = useTimeout(500, mounted); const combinedTemporalDimension = useCombinedTemporalDimension(); - const sliderRange = useMemo(() => { - const { values } = combinedTemporalDimension; - const min = values[0]?.value; - const max = values[values.length - 1]?.value; - - if (!min || !max) { - return; - } - - return [ - toUnixSeconds(parseDate(min as string)), - toUnixSeconds(parseDate(max as string)), - ]; + const markValues = useMemo(() => { + return combinedTemporalDimension.values.map(({ value }) => + toUnixSeconds(parseDate(value as string)) + ); }, [combinedTemporalDimension]); + const formatIndex = (index: number) => valueLabelFormat(markValues[index]); - if (!timeRange || !filter.active || !sliderRange) { + if (!timeRange || !filter.active || markValues.length === 0) { return null; } return ( handleChangeSlider(value)} + onChange={(_ev, value) => { + handleChangeSlider( + (Array.isArray(value) ? value : [value]).map((i) => markValues[i]) + ); + }} onChangeCommitted={() => setEnableTransition(true)} - valueLabelFormat={valueLabelFormat} - step={null} - min={sliderRange[0]} - max={sliderRange[1]} + getAriaLabel={(index) => + index === 0 + ? t({ id: "controls.filters.select.from", message: "From" }) + : t({ id: "controls.filters.select.to", message: "To" }) + } + getAriaValueText={formatIndex} + valueLabelFormat={formatIndex} + step={1} + min={0} + max={markValues.length - 1} valueLabelDisplay={mountedForSomeTime ? "on" : "off"} - value={timeRange} - marks={combinedTemporalDimension.values.map(({ value }) => ({ - value: toUnixSeconds(parseDate(value as string)), - }))} + value={timeRange.map((value) => getClosestIndex(markValues, value))} + marks={markValues.map((_, i) => ({ value: i }))} /> ); }; +const getClosestIndex = (values: number[], value: number) => { + const i = bisectLeft(values, value); + + if (i === 0) { + return 0; + } + + if (i === values.length) { + return values.length - 1; + } + + return value - values[i - 1] < values[i] - value ? i - 1 : i; +}; + const useDataFilterStyles = makeStyles((theme: Theme) => ({ wrapper: { display: "grid", diff --git a/app/configurator/interactive-filters/editor-brush.tsx b/app/configurator/interactive-filters/editor-brush.tsx index c6f888baff..a8975b6110 100644 --- a/app/configurator/interactive-filters/editor-brush.tsx +++ b/app/configurator/interactive-filters/editor-brush.tsx @@ -116,7 +116,7 @@ export const EditorBrush = ({
{width > 0 && ( - + { return ( - + ); }; -const Root = ({ showPlayButton }: { showPlayButton: boolean }) => { +const Root = ({ + showPlayButton, + label, +}: { + showPlayButton: boolean; + label: string; +}) => { const timeline = useTimeline(); const [min, max] = timeline.formattedExtent; @@ -171,7 +178,7 @@ const Root = ({ showPlayButton }: { showPlayButton: boolean }) => { > {showPlayButton && }
- + { return (
@@ -495,6 +512,13 @@ const LinkDialog = () => { className={classes.popper} open={state.type !== "inactive"} anchorEl={editorRootElementRef?.current} + role="dialog" + aria-label={ + maybeDelayedState.type === "edit" + ? t({ id: "mdx-editor.link-dialog.edit", message: "Edit link" }) + : t({ id: "mdx-editor.link-dialog.preview", message: "Link" }) + } + disablePortal transition sx={{ top: `${(rectangle?.top ?? 0) + (rectangle?.height ?? 0) + 4}px !important`, @@ -505,12 +529,16 @@ const LinkDialog = () => { {maybeDelayedState.type === "edit" && ( - + +
+ +
+
)} {maybeDelayedState.type === "preview" && ( <> @@ -529,6 +557,10 @@ const LinkDialog = () => { ) : null} { switchFromPreviewToLinkEdit(); }} diff --git a/app/components/mdx-editor/list-toggles.tsx b/app/components/mdx-editor/list-toggles.tsx index b324395b5a..2ca4fccaea 100644 --- a/app/components/mdx-editor/list-toggles.tsx +++ b/app/components/mdx-editor/list-toggles.tsx @@ -1,3 +1,4 @@ +import { t } from "@lingui/macro"; import { applyListType$, currentListType$ } from "@mdxeditor/editor"; import { useCellValues, usePublisher } from "@mdxeditor/gurx"; @@ -7,9 +8,11 @@ import { Icon, IconName } from "@/icons"; const ListTypeButton = ({ listType, iconName, + label, }: { listType: "bullet" | "number"; iconName: IconName; + label: string; }) => { const [currentListType] = useCellValues(currentListType$); const applyListType = usePublisher(applyListType$); @@ -17,15 +20,11 @@ const ListTypeButton = ({ return ( { applyListType(active ? "" : listType); }} - sx={{ - backgroundColor: active ? "grey.300" : "transparent", - "&:hover": { - backgroundColor: active ? "grey.300" : "grey.200", - }, - }} > @@ -35,8 +34,22 @@ const ListTypeButton = ({ export const ListToggles = () => { return ( <> - - + + ); }; diff --git a/app/components/mdx-editor/tab-navigation-plugin.tsx b/app/components/mdx-editor/tab-navigation-plugin.tsx new file mode 100644 index 0000000000..77d1c2e66b --- /dev/null +++ b/app/components/mdx-editor/tab-navigation-plugin.tsx @@ -0,0 +1,14 @@ +import { createRootEditorSubscription$, realmPlugin } from "@mdxeditor/editor"; +import { COMMAND_PRIORITY_CRITICAL, KEY_TAB_COMMAND } from "lexical"; + +export const tabNavigationPlugin = realmPlugin({ + init: (realm) => { + realm.pub(createRootEditorSubscription$, (editor) => { + return editor.registerCommand( + KEY_TAB_COMMAND, + () => true, + COMMAND_PRIORITY_CRITICAL + ); + }); + }, +}); diff --git a/app/components/mdx-editor/toolbar-buttons.spec.tsx b/app/components/mdx-editor/toolbar-buttons.spec.tsx new file mode 100644 index 0000000000..c9b522fd43 --- /dev/null +++ b/app/components/mdx-editor/toolbar-buttons.spec.tsx @@ -0,0 +1,50 @@ +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it } from "vitest"; + +import { ToolbarButtons } from "@/components/mdx-editor/toolbar-buttons"; + +afterEach(cleanup); + +const renderToolbar = () => { + render( + + + + + + ); + + return ["Bold", "Italic", "Underline"].map((name) => + screen.getByRole("button", { name }) + ); +}; + +describe("ToolbarButtons", () => { + it("keeps a single tab stop", () => { + const buttons = renderToolbar(); + + expect(buttons.map((b) => b.tabIndex)).toEqual([0, -1, -1]); + }); + + it("moves focus with arrow keys, Home and End", () => { + const [bold, italic, underline] = renderToolbar(); + bold.focus(); + + fireEvent.keyDown(bold, { key: "ArrowRight" }); + expect(document.activeElement).toBe(italic); + expect(italic.tabIndex).toBe(0); + expect(bold.tabIndex).toBe(-1); + + fireEvent.keyDown(italic, { key: "End" }); + expect(document.activeElement).toBe(underline); + + fireEvent.keyDown(underline, { key: "ArrowRight" }); + expect(document.activeElement).toBe(bold); + + fireEvent.keyDown(bold, { key: "ArrowLeft" }); + expect(document.activeElement).toBe(underline); + + fireEvent.keyDown(underline, { key: "Home" }); + expect(document.activeElement).toBe(bold); + }); +}); diff --git a/app/components/mdx-editor/toolbar-buttons.tsx b/app/components/mdx-editor/toolbar-buttons.tsx new file mode 100644 index 0000000000..6abeee2382 --- /dev/null +++ b/app/components/mdx-editor/toolbar-buttons.tsx @@ -0,0 +1,73 @@ +import { FocusEvent, KeyboardEvent, ReactNode, useEffect, useRef } from "react"; + +import { Flex } from "@/components/flex"; + +const KEY_TO_INDEX: Record< + string, + ((index: number, count: number) => number) | undefined +> = { + ArrowRight: (index, count) => (index + 1) % count, + ArrowLeft: (index, count) => (index - 1 + count) % count, + Home: () => 0, + End: (_, count) => count - 1, +}; + +export const ToolbarButtons = ({ children }: { children: ReactNode }) => { + const ref = useRef(null); + const activeIndexRef = useRef(0); + + useEffect(() => { + if (ref.current) { + setRovingTabIndex(getButtons(ref.current), activeIndexRef.current); + } + }); + + const handleFocus = (e: FocusEvent) => { + const buttons = getButtons(e.currentTarget); + const index = buttons.findIndex( + (button) => button === document.activeElement + ); + + if (index !== -1) { + activeIndexRef.current = index; + setRovingTabIndex(buttons, index); + } + }; + + const handleKeyDown = (e: KeyboardEvent) => { + const buttons = getButtons(e.currentTarget); + const index = buttons.findIndex( + (button) => button === document.activeElement + ); + const getNextIndex = KEY_TO_INDEX[e.key]; + + if (index === -1 || !getNextIndex) { + return; + } + + e.preventDefault(); + buttons[getNextIndex(index, buttons.length)].focus(); + }; + + return ( + + {children} + + ); +}; + +const getButtons = (container: HTMLElement) => { + return Array.from( + container.querySelectorAll("button:not(:disabled)") + ); +}; + +const setRovingTabIndex = ( + buttons: HTMLButtonElement[], + activeIndex: number +) => { + const index = Math.min(activeIndex, buttons.length - 1); + buttons.forEach((button, i) => { + button.tabIndex = i === index ? 0 : -1; + }); +}; diff --git a/app/configurator/components/chart-annotations/chart-annotations-selector.tsx b/app/configurator/components/chart-annotations/chart-annotations-selector.tsx index 73a20a48c4..802e0042a9 100644 --- a/app/configurator/components/chart-annotations/chart-annotations-selector.tsx +++ b/app/configurator/components/chart-annotations/chart-annotations-selector.tsx @@ -196,12 +196,18 @@ export const ChartAnnotationsSelector = () => { - + { id: "controls.fill-and-text", message: "Fill and text box", })} + name={`annotation-${annotation.key}-style`} value="filled" checked={annotation.highlightType === "filled"} disabled={!hasTargets} @@ -344,7 +351,7 @@ const AnnotationDrawer = ({ } return ( - +
(); const handleOpenCreateColorPalette: MouseEventHandler = useEvent( (e) => { @@ -224,10 +226,11 @@ export const ColorPalette = ({ }} >
-
); diff --git a/app/components/skip-link.tsx b/app/components/skip-link.tsx new file mode 100644 index 0000000000..3af65bb46f --- /dev/null +++ b/app/components/skip-link.tsx @@ -0,0 +1,32 @@ +import { t } from "@lingui/macro"; +import { Link, Theme } from "@mui/material"; +import { makeStyles } from "@mui/styles"; + +export const MAIN_CONTENT_ID = "main"; + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + position: "absolute", + top: theme.spacing(2), + left: theme.spacing(2), + zIndex: theme.zIndex.tooltip + 1, + padding: theme.spacing(2, 4), + backgroundColor: theme.palette.background.paper, + ...theme.typography.body2, + transform: "translateY(-200%)", + + "&:focus": { + transform: "none", + }, + }, +})); + +export const SkipLink = () => { + const classes = useStyles(); + + return ( + + {t({ id: "skip-link.main-content", message: "Skip to main content" })} + + ); +}; diff --git a/app/configurator/components/chart-configurator.tsx b/app/configurator/components/chart-configurator.tsx index e720bb67ae..c8078a6554 100644 --- a/app/configurator/components/chart-configurator.tsx +++ b/app/configurator/components/chart-configurator.tsx @@ -157,6 +157,7 @@ export const DataFilterSelectGeneric = ({ data?.dataCubesComponents?.dimensions.find( (d) => d.cubeIri === rawDimension.cubeIri ) ?? rawDimension; + const { label } = dimension; const sharedProps = { dimension, @@ -180,6 +181,10 @@ export const DataFilterSelectGeneric = ({ diff --git a/app/configurator/components/dataset-control-section.tsx b/app/configurator/components/dataset-control-section.tsx index 0e4c55e9d9..603d01dacb 100644 --- a/app/configurator/components/dataset-control-section.tsx +++ b/app/configurator/components/dataset-control-section.tsx @@ -86,6 +86,7 @@ const DatasetRow = ({ const classes = useStyles(); const [state, dispatch] = useConfiguratorState(isConfiguring); const [loading, setLoading] = useState(false); + const { title } = cube; return ( Dataset - {cube.title} + {title}
{canRemove ? ( { try { const chartConfig = getChartConfig(state); diff --git a/app/locales/de/messages.po b/app/locales/de/messages.po index 6a689ebd71..4b9e0671c8 100644 --- a/app/locales/de/messages.po +++ b/app/locales/de/messages.po @@ -76,11 +76,6 @@ msgstr "Divergierend" msgid "controls.custom-color-palettes.categorical" msgstr "Kategorisch" -#: app/configurator/components/chart-options-selector/imputation-field.tsx -#: app/configurator/components/field-i18n.ts -msgid "controls.imputation.type.none" -msgstr "-" - #: app/configurator/components/annotators.tsx msgid "annotator.add.description" msgstr "[ Beschreibung hinzufügen ]" @@ -106,6 +101,10 @@ msgstr "{0} weitere" msgid "controls.filter.nb-elements" msgstr "{0} von {1}" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-results" +msgstr "{resultsCount} Ergebnisse" + #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" msgstr "1 → 9" @@ -132,6 +131,11 @@ msgstr "A → Z" msgid "footer.about_us.label" msgstr "Über dieses Portal" +#: app/components/chart-selection-tabs.tsx +#: app/components/row-actions.tsx +msgid "menu.actions-for" +msgstr "Aktionen für {label}" + #: app/configurator/components/chart-annotations/add-annotation-button.tsx msgid "controls.annotations.add" msgstr "Hinzufügen" @@ -140,6 +144,7 @@ msgstr "Hinzufügen" msgid "controls.add.chart" msgstr "Fügen Sie ein weiteres Diagramm hinzu." +#: app/components/chart-selection-tabs.tsx #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart" msgstr "Diagramm hinzufügen" @@ -230,6 +235,7 @@ msgstr "Beim Anzeigen dieses Diagramms ist ein unerwarteter Fehler aufgetreten." msgid "controls.animation" msgstr "Animation" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.duration" msgstr "Animation Dauer" @@ -242,6 +248,7 @@ msgstr "Die Animation ist bei der Sortierung nach Gesamtgrösse deaktiviert." msgid "controls.section.animation.settings" msgstr "Animation Einstellungen" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.type" msgstr "Animation Typ" @@ -358,6 +365,10 @@ msgstr "Zurück" msgid "controls.nav.back-to-configurator" msgstr "Zurück zum Editieren" +#: app/configurator/components/chart-controls/section.tsx +msgid "controls.section.back-to-main" +msgstr "Zurück zur Übersicht" + #: app/configurator/components/configurator.tsx msgid "controls.nav.back-to-preview" msgstr "Zurück zur Vorschau" @@ -396,6 +407,10 @@ msgstr "Basis-URL" msgid "chart.map.layers.base.behind-area-layer" msgstr "Hinter der Flächenebene" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.bold" +msgstr "Fett" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.textStyle.bold" msgstr "Fett" @@ -404,6 +419,10 @@ msgstr "Fett" msgid "browse.dataset.all" msgstr "Datensatz suchen" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.bullet-list" +msgstr "Aufzählung" + #: app/charts/map/wms-wmts-provider-info.tsx msgid "provider-info.can-display-layers" msgstr "Kann die Layer anzeigen" @@ -416,6 +435,10 @@ msgstr "Kann auf der Geo Admin Karte angezeigt werden" msgid "provider-info.can-list-layers" msgstr "Kann die Layer auflisten" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.cancel" +msgstr "Abbrechen" + #: app/components/chart-published.tsx msgid "dataset.publicationStatus.expires.warning" msgstr "Achtung, dieser Datensatz ist abgelaufen.<0/><1>Diese Grafik nicht für Berichte verwenden." @@ -442,6 +465,7 @@ msgstr "Diagramm" msgid "button.download.data.visible" msgstr "Daten aus der Grafik" +#: app/configurator/components/chart-options-selector/calculation-field.tsx #: app/configurator/components/chart-options-selector/calculation-field.tsx msgid "controls.select.calculation.mode" msgstr "Visualisierungsmodus" @@ -456,6 +480,7 @@ msgstr "Diagramm-Einstellungen" msgid "controls.select.chart.type" msgstr "Diagrammtyp" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.share.chart.url" msgstr "Diagramm-URL" @@ -473,6 +498,7 @@ msgstr "Weitere Informationen finden Sie auf der Statusseite" msgid "controls.custom-color-palettes.caption-diverging" msgstr "Wählen Sie kontrastierende Farben für Start- und Endpunkt. Diese Farben unterstützen die visuelle Trennung von Extremwerten. Divergierende Paletten eignen sich besonders für Daten mit einem bedeutsamen Mittelpunkt wie Null oder einem Durchschnittswert." +#: app/charts/map/wms-wmts-selector.tsx #: app/components/form.tsx #: app/configurator/components/add-dataset-drawer/add-dataset-drawer.tsx msgid "controls.search.clear" @@ -487,11 +513,16 @@ msgstr "Auswahl löschen" msgid "controls.annotations.annotation.clear-text" msgstr "Text löschen" -#: app/components/copy-to-clipboard-text-input.tsx #: app/components/copy-to-clipboard-text-input.tsx msgid "button.hint.click.to.copy" msgstr "Kopieren" +#: app/charts/map/wms-wmts-selector.tsx +#: app/configurator/components/chart-controls/drawer-color-palette-content.tsx +#: app/configurator/components/filters.tsx +msgid "button.close" +msgstr "Schliessen" + #: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/conversion-units-field.tsx #: app/configurator/components/field-i18n.ts @@ -503,6 +534,10 @@ msgstr "Farbe" msgid "controls.color.palette" msgstr "Farbpalette" +#: app/configurator/components/chart-controls/color-picker.tsx +msgid "controls.colorpicker" +msgstr "Farbauswahl" + #: app/configurator/components/chart-controls/drag-and-drop-tab.tsx msgid "table.column.no" msgstr "Spalte {0}" @@ -511,6 +546,7 @@ msgstr "Spalte {0}" msgid "controls.select.columnStyle.columnColor" msgstr "Spaltenfarbe" +#: app/configurator/components/chart-options-selector/layout-field.tsx #: app/configurator/components/chart-options-selector/layout-field.tsx msgid "controls.select.column.layout" msgstr "Spaltenstil" @@ -599,6 +635,10 @@ msgstr "Diese Visualisierung kopieren und editieren" msgid "button.copy-preview-link" msgstr "Vorschau-Link kopieren" +#: app/components/copy-to-clipboard-text-input.tsx +msgid "button.copy" +msgstr "Kopieren: {label}" + #: app/utils/flashes.tsx msgid "flashes.couldnt-load-cube.title" msgstr "Der Cube konnte nicht geladen werden" @@ -817,6 +857,11 @@ msgstr "Farbplatte editieren" msgid "controls.set-filters" msgstr "Filter bearbeiten" +#: app/components/mdx-editor/link-dialog.tsx +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.edit" +msgstr "Link bearbeiten" + #: app/components/chart-selection-tabs.tsx msgid "chart-controls.edit-tab-label" msgstr "Tab-Titel editieren" @@ -829,6 +874,7 @@ msgstr "Einbetten" msgid "login.chart.embed" msgstr "Einbetten" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.external-application" msgstr "Embed-Code für «Externe Applikation»" @@ -858,6 +904,10 @@ msgstr "Englisch" msgid "controls.custom-color-palettes.example" msgstr "Beispiel" +#: app/configurator/components/filters.tsx +msgid "controls.filters.tree.expand" +msgstr "{label} aufklappen" + #: app/browse/ui/select-dataset-banner.tsx msgid "browse.datasets.description" msgstr "Erkunden Sie die vom LINDAS Linked Data Service bereitgestellten Datensätze, indem Sie entweder nach Kategorien oder Organisationen filtern oder direkt nach bestimmten Stichworten suchen. Klicken Sie auf einen Datensatz, um detailliertere Informationen zu erhalten und Ihre eigenen Visualisierungen zu erstellen." @@ -920,6 +970,8 @@ msgstr "Freies Layout" msgid "controls.language.french" msgstr "Französisch" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -963,6 +1015,10 @@ msgstr "Gruppiert" msgid "controls.section.groups" msgstr "Gruppierungen" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.heading" +msgstr "Überschrift {n}" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.heatmap" msgstr "Heatmap" @@ -971,6 +1027,10 @@ msgstr "Heatmap" msgid "publication.share.mail.body" msgstr "Hier ist eine Visualisierung, die ich mit visualize.admin.ch erstellt habe" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hex" +msgstr "Hex-Farbe" + #: app/configurator/table/table-chart-options.tsx msgid "controls.table.column.hide" msgstr "Spalte ausblenden" @@ -1003,6 +1063,11 @@ msgstr "Horizontale Achse" msgid "time-units.Hour" msgstr "Stunde" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hue" +msgstr "Farbton" + +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.iframe" msgstr "Embed-Code" @@ -1040,6 +1105,10 @@ msgstr "Ungültige URL" msgid "controls.language.italian" msgstr "Italienisch" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.italic" +msgstr "Kursiv" + #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.color.scale.discretization.jenks" msgstr "Jenks (Natural Breaks)" @@ -1074,6 +1143,10 @@ msgstr "Label" msgid "mdx-editor.link-dialog.label-placeholder" msgstr "Label..." +#: app/components/header.tsx +msgid "controls.language" +msgstr "Sprache" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.large" msgstr "Gross" @@ -1124,6 +1197,10 @@ msgstr "Messung der linken Achse" msgid "wmts.legend-title" msgstr "Legende" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.legend-for" +msgstr "Legende: {title}" + #: app/components/footer.tsx msgid "footer.button.lindas" msgstr "LINDAS Linked Data Dienste" @@ -1137,6 +1214,18 @@ msgstr "Lineare Interpolation" msgid "controls.chart.type.line" msgstr "Linien" +#: app/components/mdx-editor/link-dialog-toggle.tsx +msgid "mdx-editor.toolbar.link" +msgstr "Link" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.preview" +msgstr "Link" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.label" +msgstr "Linktext" + #: app/configurator/table/configurator/links-section.tsx msgid "controls.section.links" msgstr "Links" @@ -1183,6 +1272,7 @@ msgstr "Min muss kleiner als Max sein" msgid "time-units.Minute" msgstr "Minute" +#: app/configurator/components/chart-options-selector/imputation-field.tsx #: app/configurator/components/chart-options-selector/imputation-field.tsx msgid "controls.section.imputation" msgstr "Fehlende Werte" @@ -1195,6 +1285,16 @@ msgstr "Monat" msgid "dataset.metadata.learnmore" msgstr "Mehr zum Datensatz" +#: app/components/chart-selection-tabs.tsx +#: app/components/chart-shared.tsx +#: app/components/dashboard-shared.tsx +msgid "menu.more-actions" +msgstr "Weitere Aktionen" + +#: app/components/info-icon-tooltip.tsx +msgid "info.more-information" +msgstr "Weitere Informationen" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.comboLineSingle" msgstr "Multi-line" @@ -1328,6 +1428,11 @@ msgstr "Mit dem ausgewählten Datensatz kann keine Visualisierung erstellt werde msgid "controls.none" msgstr "Keine" +#: app/configurator/components/chart-options-selector/imputation-field.tsx +#: app/configurator/components/field-i18n.ts +msgid "controls.imputation.type.none" +msgstr "Keine" + #: app/configurator/components/field-i18n.ts msgid "controls.calculation.identity" msgstr "Normal" @@ -1345,6 +1450,10 @@ msgstr "Beachten Sie, dass Sie nur Messungen mit der gleichen Einheit kombiniere msgid "controls.color.scale.number.of.classes" msgstr "Anzahl Klassen" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.number-list" +msgstr "Nummerierte Liste" + #: app/configurator/components/layout-configurator.tsx msgid "controls.section.block-options" msgstr "Objekte" @@ -1362,8 +1471,8 @@ msgid "controls.color.opacity" msgstr "Transparenz" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.open" -msgstr "Farbauswahl öffnen" +msgid "controls.colorpicker.open-for" +msgstr "Farbauswahl öffnen: {label}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.optimize-space" @@ -1381,10 +1490,22 @@ msgstr "Organisationen" msgid "controls.convert-unit.original" msgstr "Ursprüngliche Einheit: {0}" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.paragraph" +msgstr "Absatz" + +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.pause" +msgstr "Pause" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.pie" msgstr "Kreis" +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.play" +msgstr "Abspielen" + #: app/configurator/components/annotators.tsx #: app/configurator/components/annotators.tsx msgid "controls.annotator.add-description-warning" @@ -1485,8 +1606,20 @@ msgid "publication.embed.iframe.remove-border" msgstr "Rand entfernen" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.remove" -msgstr "Farbe entfernen" +msgid "controls.colorpicker.remove-for" +msgstr "Farbe entfernen: {label}" + +#: app/configurator/components/dataset-control-section.tsx +msgid "controls.dataset.remove" +msgstr "Datensatz entfernen: {title}" + +#: app/configurator/components/chart-configurator.tsx +msgid "controls.filters.remove" +msgstr "Filter entfernen: {label}" + +#: app/configurator/components/custom-layers-selector.tsx +msgid "chart.map.layers.remove" +msgstr "Ebene entfernen: {title}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.remove-more-options-button" @@ -1504,6 +1637,10 @@ msgstr "Umbenennen" msgid "profile.chart.rename-dialog.title" msgstr "Benennen Sie die Visualisierung um" +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.table.sorting.reorder" +msgstr "Neu anordnen: {label}" + #: app/login/components/login-menu.tsx msgid "login.profile.bug-report" msgstr "Fehler melden" @@ -1516,10 +1653,15 @@ msgstr "Funktion wünschen" msgid "controls.color.palette.reset" msgstr "Farbpalette zurücksetzen" +#: app/configurator/components/filters.tsx #: app/configurator/components/filters.tsx msgid "controls.filters.select.reset-colors" msgstr "Farben zurücksetzen" +#: app/charts/map/map.tsx +msgid "chart.map.reset" +msgstr "Kartenansicht zurücksetzen" + #: app/configurator/table/table-chart-configurator.tsx msgid "controls.tableSettings.limitColumnWidths" msgstr "Responsive Spaltenbreiten" @@ -1544,6 +1686,18 @@ msgstr "Rechte Achse (Linie)" msgid "controls.chart.combo.y.right-axis-measure" msgstr "Messung der rechten Achse" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation.value" +msgstr "Sättigung {saturation} %, Helligkeit {brightness} %" + +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation" +msgstr "Sättigung und Helligkeit" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.save" +msgstr "Speichern" + #: app/login/components/color-palettes/profile-color-palette-content.tsx msgid "login.profile.my-color-palettes.description" msgstr "Speichern Sie eine benutzerdefinierte Farbpalette, damit Sie Farben für Ihre Visualisierungen einfach auswählen können." @@ -1556,6 +1710,7 @@ msgstr "Farbpalette speichern" msgid "button.save-draft" msgstr "Entwurf speichern" +#: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.scale.type" msgstr "Skalierungstyp" @@ -1571,10 +1726,18 @@ msgstr "Scatterplot" msgid "dataset.search.label" msgstr "Suchen" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-label" +msgstr "In importierten Karten suchen" + #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-placeholder" msgstr "Suche in importierten Karten..." +#: app/charts/table/table.tsx +msgid "table.search.label" +msgstr "Tabelle durchsuchen" + #: app/charts/table/table.tsx msgid "table.search.placeholder" msgstr "Suchen..." @@ -1604,6 +1767,7 @@ msgstr "Farbe auswählen" msgid "controls.color.palette.select" msgstr "Wähle eine Farbpalette" +#: app/login/components/color-palettes/profile-color-palette-form.tsx #: app/login/components/color-palettes/profile-color-palette-form.tsx msgid "controls.custom-color-palettes.type" msgstr "Wählen Sie einen Farbpalettentyp." @@ -1633,6 +1797,7 @@ msgstr "Wählen Sie eine Dimension aus..." msgid "controls.select.measure" msgstr "Messwert auswählen" +#: app/configurator/components/chart-options-selector/show-dots-field.tsx #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size" msgstr "Wählen Sie eine Grösse" @@ -1666,6 +1831,7 @@ msgstr "Wählen Sie einen Datensatz mit gemeinsamen Dimensionen aus" msgid "controls.dimensionvalue.select" msgstr "Filter auswählen" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.line-type" msgstr "Linientyp auswählen" @@ -1674,6 +1840,7 @@ msgstr "Linientyp auswählen" msgid "controls.filter.select.none" msgstr "Alle abwählen" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.symbol-type" msgstr "Symboltyp auswählen" @@ -1822,6 +1989,10 @@ msgstr "Abmelden" msgid "controls.size" msgstr "Grösse" +#: app/components/skip-link.tsx +msgid "skip-link.main-content" +msgstr "Zum Hauptinhalt springen" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.small" msgstr "Klein" @@ -1855,6 +2026,11 @@ msgstr "Sortierung" msgid "controls.sorting.byTotalSize.disabled-by-animation" msgstr "Die Sortierung nach Gesamtgrösse ist während der Animation deaktiviert." +#: app/configurator/components/chart-options-selector/sorting-field.tsx +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.sorting.order" +msgstr "Sortierreihenfolge" + #: app/components/dataset-metadata.tsx msgid "dataset.metadata.source" msgstr "Quelle" @@ -1905,6 +2081,7 @@ msgstr "Informiert bleiben" msgid "controls.section.animation.type.stepped" msgstr "Abgestuft" +#: app/configurator/components/chart-annotations/chart-annotations-selector.tsx #: app/configurator/components/chart-annotations/chart-annotations-selector.tsx msgid "controls.annotations.highlight.section.style.title" msgstr "Stil" @@ -1953,6 +2130,7 @@ msgstr "Hoch" msgid "controls.tableSettings.targetComponentId" msgstr "Zielspalte" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values" msgstr "Zielvorgaben und Grenzwerte" @@ -1975,6 +2153,10 @@ msgstr "Schriftfarbe" msgid "controls.section.block-options.block.text" msgstr "Text Objekt" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.block-type" +msgstr "Textstil" + #: app/configurator/table/table-chart-options.tsx msgid "controls.select.columnStyle.textStyle" msgstr "Schriftstil" @@ -2047,6 +2229,8 @@ msgstr "Titel hinzufügen" msgid "controls.section.description" msgstr "Titel & Beschreibung" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -2073,6 +2257,10 @@ msgstr "Tutorials" msgid "login.profile.my-visualizations.chart-type" msgstr "Typ" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.underline" +msgstr "Unterstrichen" + #: app/configurator/components/add-dataset-drawer/caution-alert.tsx msgid "dataset.search.caution.acknowledge" msgstr "Verstanden, ich werde vorsichtig vorgehen." @@ -2097,6 +2285,10 @@ msgstr "Farbpalette aktualisieren" msgid "button.update" msgstr "Diese Visualisierung aktualisieren" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.url" +msgstr "URL" + #: app/components/mdx-editor/link-dialog.tsx msgid "mdx-editor.link-dialog.url-placeholder" msgstr "URL..." @@ -2214,3 +2406,11 @@ msgstr "Z → A" #: app/configurator/components/field-i18n.ts msgid "controls.imputation.type.zeros" msgstr "Nullen" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-in" +msgstr "Vergrössern" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-out" +msgstr "Verkleinern" diff --git a/app/locales/en/messages.po b/app/locales/en/messages.po index cf337ef206..40ef0607c1 100644 --- a/app/locales/en/messages.po +++ b/app/locales/en/messages.po @@ -76,11 +76,6 @@ msgstr "Diverging" msgid "controls.custom-color-palettes.categorical" msgstr "Categorical" -#: app/configurator/components/chart-options-selector/imputation-field.tsx -#: app/configurator/components/field-i18n.ts -msgid "controls.imputation.type.none" -msgstr "-" - #: app/configurator/components/annotators.tsx msgid "annotator.add.description" msgstr "[ Add Description ]" @@ -106,6 +101,10 @@ msgstr "{0} more" msgid "controls.filter.nb-elements" msgstr "{0} of {1}" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-results" +msgstr "{resultsCount} results" + #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" msgstr "1 → 9" @@ -132,6 +131,11 @@ msgstr "A → Z" msgid "footer.about_us.label" msgstr "About Us" +#: app/components/chart-selection-tabs.tsx +#: app/components/row-actions.tsx +msgid "menu.actions-for" +msgstr "Actions for {label}" + #: app/configurator/components/chart-annotations/add-annotation-button.tsx msgid "controls.annotations.add" msgstr "Add" @@ -140,6 +144,7 @@ msgstr "Add" msgid "controls.add.chart" msgstr "Add another chart." +#: app/components/chart-selection-tabs.tsx #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart" msgstr "Add chart" @@ -230,6 +235,7 @@ msgstr "An unexpected error occurred while displaying this chart." msgid "controls.animation" msgstr "Animation" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.duration" msgstr "Animation Duration" @@ -242,6 +248,7 @@ msgstr "Animation is disabled when sorting by total size." msgid "controls.section.animation.settings" msgstr "Animation Settings" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.type" msgstr "Animation Type" @@ -358,6 +365,10 @@ msgstr "Back" msgid "controls.nav.back-to-configurator" msgstr "Back to editing" +#: app/configurator/components/chart-controls/section.tsx +msgid "controls.section.back-to-main" +msgstr "Back to main" + #: app/configurator/components/configurator.tsx msgid "controls.nav.back-to-preview" msgstr "Back to preview" @@ -396,6 +407,10 @@ msgstr "Base URL" msgid "chart.map.layers.base.behind-area-layer" msgstr "Behind area layer" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.bold" +msgstr "Bold" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.textStyle.bold" msgstr "Bold" @@ -404,6 +419,10 @@ msgstr "Bold" msgid "browse.dataset.all" msgstr "Browse all datasets" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.bullet-list" +msgstr "Bulleted list" + #: app/charts/map/wms-wmts-provider-info.tsx msgid "provider-info.can-display-layers" msgstr "Can display layers" @@ -416,6 +435,10 @@ msgstr "Can display on map.geo.admin.ch" msgid "provider-info.can-list-layers" msgstr "Can list layers" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.cancel" +msgstr "Cancel" + #: app/components/chart-published.tsx msgid "dataset.publicationStatus.expires.warning" msgstr "Careful, the data for this chart has expired.<0/><1>Don't use for reporting!" @@ -442,6 +465,7 @@ msgstr "Chart" msgid "button.download.data.visible" msgstr "Chart dataset" +#: app/configurator/components/chart-options-selector/calculation-field.tsx #: app/configurator/components/chart-options-selector/calculation-field.tsx msgid "controls.select.calculation.mode" msgstr "Chart mode" @@ -456,6 +480,7 @@ msgstr "Chart Options" msgid "controls.select.chart.type" msgstr "Chart Type" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.share.chart.url" msgstr "Chart URL" @@ -473,6 +498,7 @@ msgstr "Check the status page for more information" msgid "controls.custom-color-palettes.caption-diverging" msgstr "Choose contrasting colors for the start and end points. These colors will help visually separate extreme values. Diverging palettes are ideal for data with a meaningful midpoint, such as zero or an average." +#: app/charts/map/wms-wmts-selector.tsx #: app/components/form.tsx #: app/configurator/components/add-dataset-drawer/add-dataset-drawer.tsx msgid "controls.search.clear" @@ -487,11 +513,16 @@ msgstr "Clear selection" msgid "controls.annotations.annotation.clear-text" msgstr "Clear text" -#: app/components/copy-to-clipboard-text-input.tsx #: app/components/copy-to-clipboard-text-input.tsx msgid "button.hint.click.to.copy" msgstr "Click to copy" +#: app/charts/map/wms-wmts-selector.tsx +#: app/configurator/components/chart-controls/drawer-color-palette-content.tsx +#: app/configurator/components/filters.tsx +msgid "button.close" +msgstr "Close" + #: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/conversion-units-field.tsx #: app/configurator/components/field-i18n.ts @@ -503,6 +534,10 @@ msgstr "Color" msgid "controls.color.palette" msgstr "Color palette" +#: app/configurator/components/chart-controls/color-picker.tsx +msgid "controls.colorpicker" +msgstr "Color picker" + #: app/configurator/components/chart-controls/drag-and-drop-tab.tsx msgid "table.column.no" msgstr "Column {0}" @@ -511,6 +546,7 @@ msgstr "Column {0}" msgid "controls.select.columnStyle.columnColor" msgstr "Column Color" +#: app/configurator/components/chart-options-selector/layout-field.tsx #: app/configurator/components/chart-options-selector/layout-field.tsx msgid "controls.select.column.layout" msgstr "Column layout" @@ -599,6 +635,10 @@ msgstr "Copy and edit this visualization" msgid "button.copy-preview-link" msgstr "Copy preview link" +#: app/components/copy-to-clipboard-text-input.tsx +msgid "button.copy" +msgstr "Copy: {label}" + #: app/utils/flashes.tsx msgid "flashes.couldnt-load-cube.title" msgstr "Could not load cube" @@ -817,6 +857,11 @@ msgstr "Edit color palette" msgid "controls.set-filters" msgstr "Edit filters" +#: app/components/mdx-editor/link-dialog.tsx +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.edit" +msgstr "Edit link" + #: app/components/chart-selection-tabs.tsx msgid "chart-controls.edit-tab-label" msgstr "Edit tab label" @@ -829,6 +874,7 @@ msgstr "Embed" msgid "login.chart.embed" msgstr "Embed" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.external-application" msgstr "Embed Code for \"External Application\"" @@ -858,6 +904,10 @@ msgstr "English" msgid "controls.custom-color-palettes.example" msgstr "Example" +#: app/configurator/components/filters.tsx +msgid "controls.filters.tree.expand" +msgstr "Expand {label}" + #: app/browse/ui/select-dataset-banner.tsx msgid "browse.datasets.description" msgstr "Explore datasets provided by the LINDAS Linked Data Service by either filtering by categories or organizations or search directly for specific keywords. Click on a dataset to see more detailed information and start creating your own visualizations." @@ -920,6 +970,8 @@ msgstr "Free canvas" msgid "controls.language.french" msgstr "French" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -963,6 +1015,10 @@ msgstr "Grouped" msgid "controls.section.groups" msgstr "Groups" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.heading" +msgstr "Heading {n}" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.heatmap" msgstr "Heat Map" @@ -971,6 +1027,10 @@ msgstr "Heat Map" msgid "publication.share.mail.body" msgstr "Here is a visualization I created using visualize.admin.ch" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hex" +msgstr "Hex color" + #: app/configurator/table/table-chart-options.tsx msgid "controls.table.column.hide" msgstr "Hide column" @@ -1003,6 +1063,11 @@ msgstr "Horizontal Axis" msgid "time-units.Hour" msgstr "Hour" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hue" +msgstr "Hue" + +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.iframe" msgstr "Embed Code" @@ -1040,6 +1105,10 @@ msgstr "Invalid URL" msgid "controls.language.italian" msgstr "Italian" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.italic" +msgstr "Italic" + #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.color.scale.discretization.jenks" msgstr "Jenks (natural breaks)" @@ -1074,6 +1143,10 @@ msgstr "Label" msgid "mdx-editor.link-dialog.label-placeholder" msgstr "Label..." +#: app/components/header.tsx +msgid "controls.language" +msgstr "Language" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.large" msgstr "Large" @@ -1124,6 +1197,10 @@ msgstr "Left axis measure" msgid "wmts.legend-title" msgstr "Legend" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.legend-for" +msgstr "Legend: {title}" + #: app/components/footer.tsx msgid "footer.button.lindas" msgstr "LINDAS Linked Data Services" @@ -1137,6 +1214,18 @@ msgstr "Linear interpolation" msgid "controls.chart.type.line" msgstr "Lines" +#: app/components/mdx-editor/link-dialog-toggle.tsx +msgid "mdx-editor.toolbar.link" +msgstr "Link" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.preview" +msgstr "Link" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.label" +msgstr "Link text" + #: app/configurator/table/configurator/links-section.tsx msgid "controls.section.links" msgstr "Links" @@ -1183,6 +1272,7 @@ msgstr "Min must be smaller than max" msgid "time-units.Minute" msgstr "Minute" +#: app/configurator/components/chart-options-selector/imputation-field.tsx #: app/configurator/components/chart-options-selector/imputation-field.tsx msgid "controls.section.imputation" msgstr "Missing values" @@ -1195,6 +1285,16 @@ msgstr "Month" msgid "dataset.metadata.learnmore" msgstr "More about the dataset" +#: app/components/chart-selection-tabs.tsx +#: app/components/chart-shared.tsx +#: app/components/dashboard-shared.tsx +msgid "menu.more-actions" +msgstr "More actions" + +#: app/components/info-icon-tooltip.tsx +msgid "info.more-information" +msgstr "More information" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.comboLineSingle" msgstr "Multi-line" @@ -1328,6 +1428,11 @@ msgstr "No visualization can be created with the selected dataset." msgid "controls.none" msgstr "None" +#: app/configurator/components/chart-options-selector/imputation-field.tsx +#: app/configurator/components/field-i18n.ts +msgid "controls.imputation.type.none" +msgstr "None" + #: app/configurator/components/field-i18n.ts msgid "controls.calculation.identity" msgstr "Normal" @@ -1345,6 +1450,10 @@ msgstr "Note that you can only combine measures of the same unit." msgid "controls.color.scale.number.of.classes" msgstr "Number of classes" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.number-list" +msgstr "Numbered list" + #: app/configurator/components/layout-configurator.tsx msgid "controls.section.block-options" msgstr "Objects" @@ -1362,8 +1471,8 @@ msgid "controls.color.opacity" msgstr "Opacity" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.open" -msgstr "Open Color Picker" +msgid "controls.colorpicker.open-for" +msgstr "Open color picker: {label}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.optimize-space" @@ -1381,10 +1490,22 @@ msgstr "Organizations" msgid "controls.convert-unit.original" msgstr "Original unit: {0}" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.paragraph" +msgstr "Paragraph" + +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.pause" +msgstr "Pause" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.pie" msgstr "Pie" +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.play" +msgstr "Play" + #: app/configurator/components/annotators.tsx #: app/configurator/components/annotators.tsx msgid "controls.annotator.add-description-warning" @@ -1485,8 +1606,20 @@ msgid "publication.embed.iframe.remove-border" msgstr "Remove border" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.remove" -msgstr "Remove color" +msgid "controls.colorpicker.remove-for" +msgstr "Remove color: {label}" + +#: app/configurator/components/dataset-control-section.tsx +msgid "controls.dataset.remove" +msgstr "Remove dataset: {title}" + +#: app/configurator/components/chart-configurator.tsx +msgid "controls.filters.remove" +msgstr "Remove filter: {label}" + +#: app/configurator/components/custom-layers-selector.tsx +msgid "chart.map.layers.remove" +msgstr "Remove layer: {title}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.remove-more-options-button" @@ -1504,6 +1637,10 @@ msgstr "Rename" msgid "profile.chart.rename-dialog.title" msgstr "Rename the visualization" +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.table.sorting.reorder" +msgstr "Reorder: {label}" + #: app/login/components/login-menu.tsx msgid "login.profile.bug-report" msgstr "Report a Bug" @@ -1516,10 +1653,15 @@ msgstr "Request a Feature" msgid "controls.color.palette.reset" msgstr "Reset color palette" +#: app/configurator/components/filters.tsx #: app/configurator/components/filters.tsx msgid "controls.filters.select.reset-colors" msgstr "Reset colors" +#: app/charts/map/map.tsx +msgid "chart.map.reset" +msgstr "Reset map view" + #: app/configurator/table/table-chart-configurator.tsx msgid "controls.tableSettings.limitColumnWidths" msgstr "Responsive column widths" @@ -1544,6 +1686,18 @@ msgstr "Right axis (line)" msgid "controls.chart.combo.y.right-axis-measure" msgstr "Right axis measure" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation.value" +msgstr "Saturation {saturation}%, brightness {brightness}%" + +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation" +msgstr "Saturation and brightness" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.save" +msgstr "Save" + #: app/login/components/color-palettes/profile-color-palette-content.tsx msgid "login.profile.my-color-palettes.description" msgstr "Save a custom color palette to easily choose your colors while creating a visualization." @@ -1556,6 +1710,7 @@ msgstr "Save color palette" msgid "button.save-draft" msgstr "Save draft" +#: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.scale.type" msgstr "Scale type" @@ -1571,10 +1726,18 @@ msgstr "Scatterplot" msgid "dataset.search.label" msgstr "Search" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-label" +msgstr "Search in imported maps" + #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-placeholder" msgstr "Search in imported maps..." +#: app/charts/table/table.tsx +msgid "table.search.label" +msgstr "Search table" + #: app/charts/table/table.tsx msgid "table.search.placeholder" msgstr "Search..." @@ -1604,6 +1767,7 @@ msgstr "Select a color" msgid "controls.color.palette.select" msgstr "Select a color palette" +#: app/login/components/color-palettes/profile-color-palette-form.tsx #: app/login/components/color-palettes/profile-color-palette-form.tsx msgid "controls.custom-color-palettes.type" msgstr "Select a color palette type" @@ -1633,6 +1797,7 @@ msgstr "Select a dimension..." msgid "controls.select.measure" msgstr "Select a measure" +#: app/configurator/components/chart-options-selector/show-dots-field.tsx #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size" msgstr "Select a Size" @@ -1666,6 +1831,7 @@ msgstr "Select dataset with shared dimensions" msgid "controls.dimensionvalue.select" msgstr "Select filter" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.line-type" msgstr "Select line type" @@ -1674,6 +1840,7 @@ msgstr "Select line type" msgid "controls.filter.select.none" msgstr "Select none" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.symbol-type" msgstr "Select symbol type" @@ -1822,6 +1989,10 @@ msgstr "Sign out" msgid "controls.size" msgstr "Size" +#: app/components/skip-link.tsx +msgid "skip-link.main-content" +msgstr "Skip to main content" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.small" msgstr "Small" @@ -1855,6 +2026,11 @@ msgstr "Sorting" msgid "controls.sorting.byTotalSize.disabled-by-animation" msgstr "Sorting by total size is disabled during animation." +#: app/configurator/components/chart-options-selector/sorting-field.tsx +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.sorting.order" +msgstr "Sorting order" + #: app/components/dataset-metadata.tsx msgid "dataset.metadata.source" msgstr "Source" @@ -1905,6 +2081,7 @@ msgstr "Stay informed" msgid "controls.section.animation.type.stepped" msgstr "Stepped" +#: app/configurator/components/chart-annotations/chart-annotations-selector.tsx #: app/configurator/components/chart-annotations/chart-annotations-selector.tsx msgid "controls.annotations.highlight.section.style.title" msgstr "Style" @@ -1953,6 +2130,7 @@ msgstr "Tall" msgid "controls.tableSettings.targetComponentId" msgstr "Target Column" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values" msgstr "Targets & limit values" @@ -1975,6 +2153,10 @@ msgstr "Text Color" msgid "controls.section.block-options.block.text" msgstr "Text object" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.block-type" +msgstr "Text style" + #: app/configurator/table/table-chart-options.tsx msgid "controls.select.columnStyle.textStyle" msgstr "Text Style" @@ -2047,6 +2229,8 @@ msgstr "Title" msgid "controls.section.description" msgstr "Title & Description" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -2073,6 +2257,10 @@ msgstr "Tutorials" msgid "login.profile.my-visualizations.chart-type" msgstr "Type" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.underline" +msgstr "Underline" + #: app/configurator/components/add-dataset-drawer/caution-alert.tsx msgid "dataset.search.caution.acknowledge" msgstr "Understood, I'll proceed cautiously." @@ -2097,6 +2285,10 @@ msgstr "Update color palette" msgid "button.update" msgstr "Update this visualization" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.url" +msgstr "URL" + #: app/components/mdx-editor/link-dialog.tsx msgid "mdx-editor.link-dialog.url-placeholder" msgstr "URL..." @@ -2214,3 +2406,11 @@ msgstr "Z → A" #: app/configurator/components/field-i18n.ts msgid "controls.imputation.type.zeros" msgstr "Zeros" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-in" +msgstr "Zoom in" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-out" +msgstr "Zoom out" diff --git a/app/locales/fr/messages.po b/app/locales/fr/messages.po index ab658751ba..5c0773a0c2 100644 --- a/app/locales/fr/messages.po +++ b/app/locales/fr/messages.po @@ -76,11 +76,6 @@ msgstr "Divergents" msgid "controls.custom-color-palettes.categorical" msgstr "Catégorique" -#: app/configurator/components/chart-options-selector/imputation-field.tsx -#: app/configurator/components/field-i18n.ts -msgid "controls.imputation.type.none" -msgstr "-" - #: app/configurator/components/annotators.tsx msgid "annotator.add.description" msgstr "[ Ajouter de description ]" @@ -106,6 +101,10 @@ msgstr "{0} autres" msgid "controls.filter.nb-elements" msgstr "{0} sur {1}" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-results" +msgstr "{resultsCount} résultats" + #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" msgstr "1 → 9" @@ -132,6 +131,11 @@ msgstr "A → Z" msgid "footer.about_us.label" msgstr "À propos de ce portail" +#: app/components/chart-selection-tabs.tsx +#: app/components/row-actions.tsx +msgid "menu.actions-for" +msgstr "Actions pour {label}" + #: app/configurator/components/chart-annotations/add-annotation-button.tsx msgid "controls.annotations.add" msgstr "Ajouter" @@ -140,6 +144,7 @@ msgstr "Ajouter" msgid "controls.add.chart" msgstr "Ajouter un autre graphique." +#: app/components/chart-selection-tabs.tsx #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart" msgstr "Ajouter un graphique" @@ -230,6 +235,7 @@ msgstr "Une erreur inattendue s'est produite lors de l'affichage de ce graphique msgid "controls.animation" msgstr "Animation" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.duration" msgstr "Durée de l'animation" @@ -242,6 +248,7 @@ msgstr "L'animation est désactivée lors du tri par taille totale." msgid "controls.section.animation.settings" msgstr "Paramètres d'animation" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.type" msgstr "Type d'animation" @@ -358,6 +365,10 @@ msgstr "Précédent" msgid "controls.nav.back-to-configurator" msgstr "Retour à l'édition" +#: app/configurator/components/chart-controls/section.tsx +msgid "controls.section.back-to-main" +msgstr "Retour à l'aperçu" + #: app/configurator/components/configurator.tsx msgid "controls.nav.back-to-preview" msgstr "Retour à l'aperçu" @@ -396,6 +407,10 @@ msgstr "URL de base" msgid "chart.map.layers.base.behind-area-layer" msgstr "Derrière la couche de surface" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.bold" +msgstr "Gras" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.textStyle.bold" msgstr "Gras" @@ -404,6 +419,10 @@ msgstr "Gras" msgid "browse.dataset.all" msgstr "Parcourir tous les ensembles de données" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.bullet-list" +msgstr "Liste à puces" + #: app/charts/map/wms-wmts-provider-info.tsx msgid "provider-info.can-display-layers" msgstr "Peut afficher les couches" @@ -416,6 +435,10 @@ msgstr "Peut afficher sur la carte GeoAdmin" msgid "provider-info.can-list-layers" msgstr "Peut lister les couches" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.cancel" +msgstr "Annuler" + #: app/components/chart-published.tsx msgid "dataset.publicationStatus.expires.warning" msgstr "Attention, ce jeu de données est expiré.<0/><1>Ne l'utilisez pas pour une publication!" @@ -442,6 +465,7 @@ msgstr "Graphique" msgid "button.download.data.visible" msgstr "Données du graphique" +#: app/configurator/components/chart-options-selector/calculation-field.tsx #: app/configurator/components/chart-options-selector/calculation-field.tsx msgid "controls.select.calculation.mode" msgstr "Mode de visualisation" @@ -456,6 +480,7 @@ msgstr "Paramètres graphiques" msgid "controls.select.chart.type" msgstr "Type de graphique" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.share.chart.url" msgstr "URL du graphique" @@ -473,6 +498,7 @@ msgstr "Consultez la page de statut pour plus d'informations" msgid "controls.custom-color-palettes.caption-diverging" msgstr "Choisissez des couleurs contrastées pour les points de départ et d'arrivée. Ces couleurs permettent de distinguer visuellement les valeurs extrêmes. Les palettes divergentes sont idéales pour les données comportant un point médian significatif, tel que zéro ou une moyenne." +#: app/charts/map/wms-wmts-selector.tsx #: app/components/form.tsx #: app/configurator/components/add-dataset-drawer/add-dataset-drawer.tsx msgid "controls.search.clear" @@ -487,11 +513,16 @@ msgstr "Sélection claire" msgid "controls.annotations.annotation.clear-text" msgstr "Effacer le texte" -#: app/components/copy-to-clipboard-text-input.tsx #: app/components/copy-to-clipboard-text-input.tsx msgid "button.hint.click.to.copy" msgstr "Cliquer pour copier" +#: app/charts/map/wms-wmts-selector.tsx +#: app/configurator/components/chart-controls/drawer-color-palette-content.tsx +#: app/configurator/components/filters.tsx +msgid "button.close" +msgstr "Fermer" + #: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/conversion-units-field.tsx #: app/configurator/components/field-i18n.ts @@ -503,6 +534,10 @@ msgstr "Couleur" msgid "controls.color.palette" msgstr "Couleurs" +#: app/configurator/components/chart-controls/color-picker.tsx +msgid "controls.colorpicker" +msgstr "Sélecteur de couleur" + #: app/configurator/components/chart-controls/drag-and-drop-tab.tsx msgid "table.column.no" msgstr "Colonne {0}" @@ -511,6 +546,7 @@ msgstr "Colonne {0}" msgid "controls.select.columnStyle.columnColor" msgstr "Couleur de la colonne" +#: app/configurator/components/chart-options-selector/layout-field.tsx #: app/configurator/components/chart-options-selector/layout-field.tsx msgid "controls.select.column.layout" msgstr "Mise en forme de la colonne" @@ -599,6 +635,10 @@ msgstr "Copier et éditer cette visualisation" msgid "button.copy-preview-link" msgstr "Copier le lien d'aperçu" +#: app/components/copy-to-clipboard-text-input.tsx +msgid "button.copy" +msgstr "Copier : {label}" + #: app/utils/flashes.tsx msgid "flashes.couldnt-load-cube.title" msgstr "Impossible de charger le cube" @@ -817,6 +857,11 @@ msgstr "Modifier la palette de couleurs" msgid "controls.set-filters" msgstr "Modifier les filtres" +#: app/components/mdx-editor/link-dialog.tsx +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.edit" +msgstr "Modifier le lien" + #: app/components/chart-selection-tabs.tsx msgid "chart-controls.edit-tab-label" msgstr "Modifier le nom de l'onglet" @@ -829,6 +874,7 @@ msgstr "Intégrer" msgid "login.chart.embed" msgstr "Intégrer" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.external-application" msgstr "Code pour intégration dans une \"Application Externe\"" @@ -858,6 +904,10 @@ msgstr "Anglais" msgid "controls.custom-color-palettes.example" msgstr "Exemple" +#: app/configurator/components/filters.tsx +msgid "controls.filters.tree.expand" +msgstr "Développer {label}" + #: app/browse/ui/select-dataset-banner.tsx msgid "browse.datasets.description" msgstr "Explorez les jeux de données liés fournis par LINDAS, en filtrant par catégories ou organisations, ou en recherchant par mots-clés. Cliquez sur un ensemble de données pour afficher des informations plus détaillées et commencer à créer vos propres visualisations. " @@ -920,6 +970,8 @@ msgstr "Mise en page libre" msgid "controls.language.french" msgstr "Français" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -963,6 +1015,10 @@ msgstr "Groupé" msgid "controls.section.groups" msgstr "Groupes" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.heading" +msgstr "Titre {n}" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.heatmap" msgstr "Heatmap" @@ -971,6 +1027,10 @@ msgstr "Heatmap" msgid "publication.share.mail.body" msgstr "Voici une visualisation que j'ai créée sur visualize.admin.ch" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hex" +msgstr "Couleur hexadécimale" + #: app/configurator/table/table-chart-options.tsx msgid "controls.table.column.hide" msgstr "Masquer la colonne" @@ -1003,6 +1063,11 @@ msgstr "Axe horizontal" msgid "time-units.Hour" msgstr "Heure" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hue" +msgstr "Teinte" + +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.iframe" msgstr "Code pour intégrer" @@ -1040,6 +1105,10 @@ msgstr "URL invalide" msgid "controls.language.italian" msgstr "Italien" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.italic" +msgstr "Italique" + #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.color.scale.discretization.jenks" msgstr "Jenks (intervalles naturels)" @@ -1074,6 +1143,10 @@ msgstr "Label" msgid "mdx-editor.link-dialog.label-placeholder" msgstr "Étiquette..." +#: app/components/header.tsx +msgid "controls.language" +msgstr "Langue" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.large" msgstr "Grand" @@ -1124,6 +1197,10 @@ msgstr "Mesure de l'axe gauche" msgid "wmts.legend-title" msgstr "Légende" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.legend-for" +msgstr "Légende : {title}" + #: app/components/footer.tsx msgid "footer.button.lindas" msgstr "Services de données liées LINDAS" @@ -1137,6 +1214,18 @@ msgstr "Interpolation linéaire" msgid "controls.chart.type.line" msgstr "Lignes" +#: app/components/mdx-editor/link-dialog-toggle.tsx +msgid "mdx-editor.toolbar.link" +msgstr "Lien" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.preview" +msgstr "Lien" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.label" +msgstr "Texte du lien" + #: app/configurator/table/configurator/links-section.tsx msgid "controls.section.links" msgstr "Links" @@ -1183,6 +1272,7 @@ msgstr "Le minimum doit être inférieur au maximum" msgid "time-units.Minute" msgstr "Minutes" +#: app/configurator/components/chart-options-selector/imputation-field.tsx #: app/configurator/components/chart-options-selector/imputation-field.tsx msgid "controls.section.imputation" msgstr "Valeurs manquantes" @@ -1195,6 +1285,16 @@ msgstr "Mois" msgid "dataset.metadata.learnmore" msgstr "En savoir plus sur le jeu de données" +#: app/components/chart-selection-tabs.tsx +#: app/components/chart-shared.tsx +#: app/components/dashboard-shared.tsx +msgid "menu.more-actions" +msgstr "Plus d'actions" + +#: app/components/info-icon-tooltip.tsx +msgid "info.more-information" +msgstr "Plus d'informations" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.comboLineSingle" msgstr "Multi-line" @@ -1328,6 +1428,11 @@ msgstr "Aucune visualisation ne peut être créée avec le jeu de données séle msgid "controls.none" msgstr "Aucun" +#: app/configurator/components/chart-options-selector/imputation-field.tsx +#: app/configurator/components/field-i18n.ts +msgid "controls.imputation.type.none" +msgstr "Aucune" + #: app/configurator/components/field-i18n.ts msgid "controls.calculation.identity" msgstr "Normal" @@ -1345,6 +1450,10 @@ msgstr "Notez que vous ne pouvez combiner que des mesures de la même unité." msgid "controls.color.scale.number.of.classes" msgstr "Nombre de classes" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.number-list" +msgstr "Liste numérotée" + #: app/configurator/components/layout-configurator.tsx msgid "controls.section.block-options" msgstr "Objets" @@ -1362,8 +1471,8 @@ msgid "controls.color.opacity" msgstr "Transparence" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.open" -msgstr "Ouvrir la pipette à couleur" +msgid "controls.colorpicker.open-for" +msgstr "Ouvrir le sélecteur de couleur : {label}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.optimize-space" @@ -1381,10 +1490,22 @@ msgstr "Organisations" msgid "controls.convert-unit.original" msgstr "Unité d'origine : {0}" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.paragraph" +msgstr "Paragraphe" + +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.pause" +msgstr "Pause" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.pie" msgstr "Secteurs" +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.play" +msgstr "Lecture" + #: app/configurator/components/annotators.tsx #: app/configurator/components/annotators.tsx msgid "controls.annotator.add-description-warning" @@ -1485,8 +1606,20 @@ msgid "publication.embed.iframe.remove-border" msgstr "Supprimer la bordure" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.remove" -msgstr "Supprimer la couleur " +msgid "controls.colorpicker.remove-for" +msgstr "Supprimer la couleur : {label}" + +#: app/configurator/components/dataset-control-section.tsx +msgid "controls.dataset.remove" +msgstr "Supprimer le jeu de données : {title}" + +#: app/configurator/components/chart-configurator.tsx +msgid "controls.filters.remove" +msgstr "Supprimer le filtre : {label}" + +#: app/configurator/components/custom-layers-selector.tsx +msgid "chart.map.layers.remove" +msgstr "Supprimer la couche : {title}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.remove-more-options-button" @@ -1504,6 +1637,10 @@ msgstr "Renommer" msgid "profile.chart.rename-dialog.title" msgstr "Renommer la visualisation" +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.table.sorting.reorder" +msgstr "Réorganiser : {label}" + #: app/login/components/login-menu.tsx msgid "login.profile.bug-report" msgstr "Signaler un bug" @@ -1516,10 +1653,15 @@ msgstr "Demande de fonctionnalité" msgid "controls.color.palette.reset" msgstr "Réinitialiser la palette de couleurs" +#: app/configurator/components/filters.tsx #: app/configurator/components/filters.tsx msgid "controls.filters.select.reset-colors" msgstr "Réinitialiser les couleurs" +#: app/charts/map/map.tsx +msgid "chart.map.reset" +msgstr "Réinitialiser la vue de la carte" + #: app/configurator/table/table-chart-configurator.tsx msgid "controls.tableSettings.limitColumnWidths" msgstr "Largeurs de colonnes adaptatives" @@ -1544,6 +1686,18 @@ msgstr "Axe droit (ligne)" msgid "controls.chart.combo.y.right-axis-measure" msgstr "Mesure de l'axe droit" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation.value" +msgstr "Saturation {saturation} %, luminosité {brightness} %" + +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation" +msgstr "Saturation et luminosité" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.save" +msgstr "Enregistrer" + #: app/login/components/color-palettes/profile-color-palette-content.tsx msgid "login.profile.my-color-palettes.description" msgstr "Enregistrez une palette de couleurs personnalisée pour sélectionner facilement les couleurs de vos visualisations." @@ -1556,6 +1710,7 @@ msgstr "Enregistrer la palette de couleurs" msgid "button.save-draft" msgstr "Sauver le brouillon" +#: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.scale.type" msgstr "Type d'échelle" @@ -1571,10 +1726,18 @@ msgstr "Nuage de points" msgid "dataset.search.label" msgstr "Chercher" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-label" +msgstr "Rechercher dans les cartes importées" + #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-placeholder" msgstr "Rechercher dans les cartes importées..." +#: app/charts/table/table.tsx +msgid "table.search.label" +msgstr "Rechercher dans le tableau" + #: app/charts/table/table.tsx msgid "table.search.placeholder" msgstr "Recherche..." @@ -1604,6 +1767,7 @@ msgstr "Sélectionner une couleur" msgid "controls.color.palette.select" msgstr "Choisir une palette de couleurs" +#: app/login/components/color-palettes/profile-color-palette-form.tsx #: app/login/components/color-palettes/profile-color-palette-form.tsx msgid "controls.custom-color-palettes.type" msgstr "Sélectionner un type de palette de couleurs" @@ -1633,6 +1797,7 @@ msgstr "Sélectionner une dimension..." msgid "controls.select.measure" msgstr "Sélectionner une variable" +#: app/configurator/components/chart-options-selector/show-dots-field.tsx #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size" msgstr "Sélectionner une taille" @@ -1666,6 +1831,7 @@ msgstr "Sélectionner un ensemble de données avec des dimensions partagées" msgid "controls.dimensionvalue.select" msgstr "Sélectionnez le filtre" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.line-type" msgstr "Sélectionner le type de ligne" @@ -1674,6 +1840,7 @@ msgstr "Sélectionner le type de ligne" msgid "controls.filter.select.none" msgstr "Tout déselectionner" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.symbol-type" msgstr "Sélectionnez le type de symbole" @@ -1822,6 +1989,10 @@ msgstr "Se déconnecter" msgid "controls.size" msgstr "Taille" +#: app/components/skip-link.tsx +msgid "skip-link.main-content" +msgstr "Aller au contenu principal" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.small" msgstr "Petit" @@ -1855,6 +2026,11 @@ msgstr "Tri" msgid "controls.sorting.byTotalSize.disabled-by-animation" msgstr "Le tri par taille totale est désactivé pendant l'animation." +#: app/configurator/components/chart-options-selector/sorting-field.tsx +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.sorting.order" +msgstr "Ordre de tri" + #: app/components/dataset-metadata.tsx msgid "dataset.metadata.source" msgstr "Source" @@ -1905,6 +2081,7 @@ msgstr "Rester informé" msgid "controls.section.animation.type.stepped" msgstr "En escalier" +#: app/configurator/components/chart-annotations/chart-annotations-selector.tsx #: app/configurator/components/chart-annotations/chart-annotations-selector.tsx msgid "controls.annotations.highlight.section.style.title" msgstr "Style" @@ -1953,6 +2130,7 @@ msgstr "Haut" msgid "controls.tableSettings.targetComponentId" msgstr "Colonne cible" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values" msgstr "Objectifs et valeurs limites" @@ -1975,6 +2153,10 @@ msgstr "Couleur du texte" msgid "controls.section.block-options.block.text" msgstr "Objet texte" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.block-type" +msgstr "Style de texte" + #: app/configurator/table/table-chart-options.tsx msgid "controls.select.columnStyle.textStyle" msgstr "Style du texte" @@ -2047,6 +2229,8 @@ msgstr "Titre" msgid "controls.section.description" msgstr "Titre & description" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -2073,6 +2257,10 @@ msgstr "Tutoriels" msgid "login.profile.my-visualizations.chart-type" msgstr "Type" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.underline" +msgstr "Souligné" + #: app/configurator/components/add-dataset-drawer/caution-alert.tsx msgid "dataset.search.caution.acknowledge" msgstr "Compris, je vais procéder avec prudence." @@ -2097,6 +2285,10 @@ msgstr "Actualiser la palette de couleurs" msgid "button.update" msgstr "Actualiser cette visualisation" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.url" +msgstr "URL" + #: app/components/mdx-editor/link-dialog.tsx msgid "mdx-editor.link-dialog.url-placeholder" msgstr "URL..." @@ -2214,3 +2406,11 @@ msgstr "Z → A" #: app/configurator/components/field-i18n.ts msgid "controls.imputation.type.zeros" msgstr "Zéros" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-in" +msgstr "Zoom avant" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-out" +msgstr "Zoom arrière" diff --git a/app/locales/it/messages.po b/app/locales/it/messages.po index 7606716933..ae4f52b618 100644 --- a/app/locales/it/messages.po +++ b/app/locales/it/messages.po @@ -76,11 +76,6 @@ msgstr "Sequenziale" msgid "controls.custom-color-palettes.categorical" msgstr "Categorico" -#: app/configurator/components/chart-options-selector/imputation-field.tsx -#: app/configurator/components/field-i18n.ts -msgid "controls.imputation.type.none" -msgstr "-" - #: app/configurator/components/annotators.tsx msgid "annotator.add.description" msgstr " [ Aggiungi descrizione ]" @@ -106,6 +101,10 @@ msgstr "altre {0}" msgid "controls.filter.nb-elements" msgstr "{0} di {1}" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-results" +msgstr "{resultsCount} risultati" + #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" msgstr "1 → 9" @@ -132,6 +131,11 @@ msgstr "A → Z" msgid "footer.about_us.label" msgstr "Informazioni su questo portale" +#: app/components/chart-selection-tabs.tsx +#: app/components/row-actions.tsx +msgid "menu.actions-for" +msgstr "Azioni per {label}" + #: app/configurator/components/chart-annotations/add-annotation-button.tsx msgid "controls.annotations.add" msgstr "Aggiungere" @@ -140,6 +144,7 @@ msgstr "Aggiungere" msgid "controls.add.chart" msgstr "Aggiungere un altro grafico." +#: app/components/chart-selection-tabs.tsx #: app/components/chart-selection-tabs.tsx msgid "chart-selection-tabs.add-chart" msgstr "Aggiungi grafico" @@ -230,6 +235,7 @@ msgstr "Si è verificato un errore imprevisto durante la visualizzazione di ques msgid "controls.animation" msgstr "Animazione" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.duration" msgstr "Durata dell'animazione" @@ -242,6 +248,7 @@ msgstr "L'animazione è disattivata quando l'ordinamento avviene per dimensione msgid "controls.section.animation.settings" msgstr "Impostazioni dell'animazione" +#: app/configurator/components/chart-options-selector/animation-field.tsx #: app/configurator/components/chart-options-selector/animation-field.tsx msgid "controls.section.animation.type" msgstr "Tipo di animazione" @@ -358,6 +365,10 @@ msgstr "Torna indietro" msgid "controls.nav.back-to-configurator" msgstr "Torniamo alla modifica" +#: app/configurator/components/chart-controls/section.tsx +msgid "controls.section.back-to-main" +msgstr "Torna alla panoramica" + #: app/configurator/components/configurator.tsx msgid "controls.nav.back-to-preview" msgstr "Torna all'anteprima" @@ -396,6 +407,10 @@ msgstr "URL di base" msgid "chart.map.layers.base.behind-area-layer" msgstr "Dietro lo strato dell'area" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.bold" +msgstr "Grassetto" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.textStyle.bold" msgstr "Grassetto" @@ -404,6 +419,10 @@ msgstr "Grassetto" msgid "browse.dataset.all" msgstr "Sfoglia tutti i set di dati" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.bullet-list" +msgstr "Elenco puntato" + #: app/charts/map/wms-wmts-provider-info.tsx msgid "provider-info.can-display-layers" msgstr "Può visualizzare i livelli" @@ -416,6 +435,10 @@ msgstr "Può visualizzare i livelli sulla mappa map.geo.admin.ch" msgid "provider-info.can-list-layers" msgstr "Può elencare i livelli" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.cancel" +msgstr "Annulla" + #: app/components/chart-published.tsx msgid "dataset.publicationStatus.expires.warning" msgstr "Attenzione, questo set di dati è scaduto.<0/><1>Non utilizzare questo grafico per un rapporto!" @@ -442,6 +465,7 @@ msgstr "Grafico" msgid "button.download.data.visible" msgstr "Dati del grafico" +#: app/configurator/components/chart-options-selector/calculation-field.tsx #: app/configurator/components/chart-options-selector/calculation-field.tsx msgid "controls.select.calculation.mode" msgstr "Modalità di visualizzazione" @@ -456,6 +480,7 @@ msgstr "Opzioni del grafico" msgid "controls.select.chart.type" msgstr "Tipo di grafico" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.share.chart.url" msgstr "URL del grafico" @@ -473,6 +498,7 @@ msgstr "Controlla la pagina di stato per ulteriori informazioni”" msgid "controls.custom-color-palettes.caption-diverging" msgstr "Scegli colori contrastanti per i punti iniziali e finali: questo aiuterà a distinguere visivamente gli estremi. Le palette divergenti sono particolarmente efficaci per i dati che hanno un punto medio significativo, come zero o un valore medio." +#: app/charts/map/wms-wmts-selector.tsx #: app/components/form.tsx #: app/configurator/components/add-dataset-drawer/add-dataset-drawer.tsx msgid "controls.search.clear" @@ -487,11 +513,16 @@ msgstr "Selezione chiara" msgid "controls.annotations.annotation.clear-text" msgstr "Cancella il testo" -#: app/components/copy-to-clipboard-text-input.tsx #: app/components/copy-to-clipboard-text-input.tsx msgid "button.hint.click.to.copy" msgstr "Clicca per copiare" +#: app/charts/map/wms-wmts-selector.tsx +#: app/configurator/components/chart-controls/drawer-color-palette-content.tsx +#: app/configurator/components/filters.tsx +msgid "button.close" +msgstr "Chiudi" + #: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/conversion-units-field.tsx #: app/configurator/components/field-i18n.ts @@ -503,6 +534,10 @@ msgstr "Colore" msgid "controls.color.palette" msgstr "Palette di colori" +#: app/configurator/components/chart-controls/color-picker.tsx +msgid "controls.colorpicker" +msgstr "Selettore di colore" + #: app/configurator/components/chart-controls/drag-and-drop-tab.tsx msgid "table.column.no" msgstr "Colonna {0}" @@ -511,6 +546,7 @@ msgstr "Colonna {0}" msgid "controls.select.columnStyle.columnColor" msgstr "Colore della colonna" +#: app/configurator/components/chart-options-selector/layout-field.tsx #: app/configurator/components/chart-options-selector/layout-field.tsx msgid "controls.select.column.layout" msgstr "Layout a colonne" @@ -599,6 +635,10 @@ msgstr "Copia e modifica questa visualizzazione" msgid "button.copy-preview-link" msgstr "Copia il collegamento di anteprima" +#: app/components/copy-to-clipboard-text-input.tsx +msgid "button.copy" +msgstr "Copia: {label}" + #: app/utils/flashes.tsx msgid "flashes.couldnt-load-cube.title" msgstr "Impossibile caricare il cubo" @@ -817,6 +857,11 @@ msgstr "Modifica colore della tavolozza" msgid "controls.set-filters" msgstr "Modificare i filtri" +#: app/components/mdx-editor/link-dialog.tsx +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.edit" +msgstr "Modifica link" + #: app/components/chart-selection-tabs.tsx msgid "chart-controls.edit-tab-label" msgstr "Modifica l'etichetta della scheda" @@ -829,6 +874,7 @@ msgstr "Incorpora" msgid "login.chart.embed" msgstr "Incorpora" +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.external-application" msgstr "Codice da incorporare per \"External Application\"" @@ -858,6 +904,10 @@ msgstr "Inglese" msgid "controls.custom-color-palettes.example" msgstr "Esempio" +#: app/configurator/components/filters.tsx +msgid "controls.filters.tree.expand" +msgstr "Espandi {label}" + #: app/browse/ui/select-dataset-banner.tsx msgid "browse.datasets.description" msgstr "Esplora i set di dati forniti dal LINDAS Linked Data Service filtrando per categorie o organizzazioni oppure cercando direttamente per parole chiave specifiche. Clicca su un set di dati per vedere informazioni più dettagliate e iniziare a creare le tue visualizzazioni." @@ -920,6 +970,8 @@ msgstr "Layout libero" msgid "controls.language.french" msgstr "Francese" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -963,6 +1015,10 @@ msgstr "Raggruppate" msgid "controls.section.groups" msgstr "Gruppi" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.heading" +msgstr "Titolo {n}" + #: app/configurator/table/table-chart-options.tsx msgid "columnStyle.heatmap" msgstr "Mappa di calore" @@ -971,6 +1027,10 @@ msgstr "Mappa di calore" msgid "publication.share.mail.body" msgstr "Ecco una visualizzazione che ho creato usando visualize.admin.ch" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hex" +msgstr "Colore esadecimale" + #: app/configurator/table/table-chart-options.tsx msgid "controls.table.column.hide" msgstr "Nascondi la colonna" @@ -1003,6 +1063,11 @@ msgstr "Asse orizzontale" msgid "time-units.Hour" msgstr "Ora" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.hue" +msgstr "Tonalità" + +#: app/components/publish-actions.tsx #: app/components/publish-actions.tsx msgid "publication.embed.iframe" msgstr "Codice di incorporamento" @@ -1040,6 +1105,10 @@ msgstr "URL non valida" msgid "controls.language.italian" msgstr "Italiano" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.italic" +msgstr "Corsivo" + #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.color.scale.discretization.jenks" msgstr "Jenks (interruzioni naturali)" @@ -1074,6 +1143,10 @@ msgstr "Etichetta" msgid "mdx-editor.link-dialog.label-placeholder" msgstr "Etichetta..." +#: app/components/header.tsx +msgid "controls.language" +msgstr "Lingua" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.large" msgstr "Grande" @@ -1124,6 +1197,10 @@ msgstr "Misura dell'asse sinistro" msgid "wmts.legend-title" msgstr "Legenda" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.legend-for" +msgstr "Legenda: {title}" + #: app/components/footer.tsx msgid "footer.button.lindas" msgstr "Servizi di dati collegati LINDAS" @@ -1137,6 +1214,18 @@ msgstr "Interpolazione lineare" msgid "controls.chart.type.line" msgstr "Linee" +#: app/components/mdx-editor/link-dialog-toggle.tsx +msgid "mdx-editor.toolbar.link" +msgstr "Link" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.preview" +msgstr "Link" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.label" +msgstr "Testo del link" + #: app/configurator/table/configurator/links-section.tsx msgid "controls.section.links" msgstr "Collegamenti" @@ -1183,6 +1272,7 @@ msgstr "Il minimo deve essere inferiore al massimo" msgid "time-units.Minute" msgstr "Minuto" +#: app/configurator/components/chart-options-selector/imputation-field.tsx #: app/configurator/components/chart-options-selector/imputation-field.tsx msgid "controls.section.imputation" msgstr "Valori mancanti" @@ -1195,6 +1285,16 @@ msgstr "Mese" msgid "dataset.metadata.learnmore" msgstr "Ulteriori informazioni sul set di dati" +#: app/components/chart-selection-tabs.tsx +#: app/components/chart-shared.tsx +#: app/components/dashboard-shared.tsx +msgid "menu.more-actions" +msgstr "Altre azioni" + +#: app/components/info-icon-tooltip.tsx +msgid "info.more-information" +msgstr "Ulteriori informazioni" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.comboLineSingle" msgstr "Multi-line" @@ -1328,6 +1428,11 @@ msgstr "Nessuna visualizzazione può essere creata con il dataset selezionato." msgid "controls.none" msgstr "Nessuno" +#: app/configurator/components/chart-options-selector/imputation-field.tsx +#: app/configurator/components/field-i18n.ts +msgid "controls.imputation.type.none" +msgstr "Nessuna" + #: app/configurator/components/field-i18n.ts msgid "controls.calculation.identity" msgstr "Normale" @@ -1345,6 +1450,10 @@ msgstr "Si noti che è possibile combinare solo misure della stessa unità." msgid "controls.color.scale.number.of.classes" msgstr "Numero di classi" +#: app/components/mdx-editor/list-toggles.tsx +msgid "mdx-editor.toolbar.number-list" +msgstr "Elenco numerato" + #: app/configurator/components/layout-configurator.tsx msgid "controls.section.block-options" msgstr "Oggetti" @@ -1362,8 +1471,8 @@ msgid "controls.color.opacity" msgstr "Trasparenza" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.open" -msgstr "Apri il selettore di colore" +msgid "controls.colorpicker.open-for" +msgstr "Apri il selettore di colore: {label}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.optimize-space" @@ -1381,10 +1490,22 @@ msgstr "Organizzazioni" msgid "controls.convert-unit.original" msgstr "Unità originale: {0}" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.paragraph" +msgstr "Paragrafo" + +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.pause" +msgstr "Pausa" + #: app/configurator/components/field-i18n.ts msgid "controls.chart.type.pie" msgstr "Torta" +#: app/configurator/interactive-filters/time-slider.tsx +msgid "controls.animation.play" +msgstr "Riproduci" + #: app/configurator/components/annotators.tsx #: app/configurator/components/annotators.tsx msgid "controls.annotator.add-description-warning" @@ -1485,8 +1606,20 @@ msgid "publication.embed.iframe.remove-border" msgstr "Rimuovere il bordo" #: app/configurator/components/chart-controls/color-picker.tsx -msgid "controls.colorpicker.remove" -msgstr "Rimuovi colore" +msgid "controls.colorpicker.remove-for" +msgstr "Rimuovi colore: {label}" + +#: app/configurator/components/dataset-control-section.tsx +msgid "controls.dataset.remove" +msgstr "Rimuovi set di dati: {title}" + +#: app/configurator/components/chart-configurator.tsx +msgid "controls.filters.remove" +msgstr "Rimuovi filtro: {label}" + +#: app/configurator/components/custom-layers-selector.tsx +msgid "chart.map.layers.remove" +msgstr "Rimuovi livello: {title}" #: app/components/publish-actions.tsx msgid "publication.embed.iframe.remove-more-options-button" @@ -1504,6 +1637,10 @@ msgstr "Rinominare" msgid "profile.chart.rename-dialog.title" msgstr "Rinomina la visualizzazione" +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.table.sorting.reorder" +msgstr "Riordina: {label}" + #: app/login/components/login-menu.tsx msgid "login.profile.bug-report" msgstr "Segnala un bug" @@ -1516,10 +1653,15 @@ msgstr "Richiedi una funzione" msgid "controls.color.palette.reset" msgstr "Ripristina la tavolozza dei colori" +#: app/configurator/components/filters.tsx #: app/configurator/components/filters.tsx msgid "controls.filters.select.reset-colors" msgstr "Reimpostare i colori" +#: app/charts/map/map.tsx +msgid "chart.map.reset" +msgstr "Reimposta la vista della mappa" + #: app/configurator/table/table-chart-configurator.tsx msgid "controls.tableSettings.limitColumnWidths" msgstr "Larghezze delle colonne reattive" @@ -1544,6 +1686,18 @@ msgstr "Asse destro (linea)" msgid "controls.chart.combo.y.right-axis-measure" msgstr "Misura dell'asse destro" +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation.value" +msgstr "Saturazione {saturation}%, luminosità {brightness}%" + +#: app/configurator/components/color-picker.tsx +msgid "controls.colorpicker.saturation" +msgstr "Saturazione e luminosità" + +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.save" +msgstr "Salva" + #: app/login/components/color-palettes/profile-color-palette-content.tsx msgid "login.profile.my-color-palettes.description" msgstr "Salvate una tavolozza di colori personalizzata per selezionare facilmente i vostri colori durante la creazione di una visualizzazione." @@ -1556,6 +1710,7 @@ msgstr "Salva tavolozza colori" msgid "button.save-draft" msgstr "Salva la bozza" +#: app/configurator/components/chart-options-selector/color-component-field.tsx #: app/configurator/components/chart-options-selector/color-component-field.tsx msgid "controls.scale.type" msgstr "Tipo di scala" @@ -1571,10 +1726,18 @@ msgstr "Scatterplot" msgid "dataset.search.label" msgstr "Cerca" +#: app/charts/map/wms-wmts-selector.tsx +msgid "wmts.search-label" +msgstr "Cerca nelle mappe importate" + #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-placeholder" msgstr "Cerca nelle mappe importate..." +#: app/charts/table/table.tsx +msgid "table.search.label" +msgstr "Cerca nella tabella" + #: app/charts/table/table.tsx msgid "table.search.placeholder" msgstr "Ricerca..." @@ -1604,6 +1767,7 @@ msgstr "Seleziona un colore" msgid "controls.color.palette.select" msgstr "Selezionare una tavolozza di colori" +#: app/login/components/color-palettes/profile-color-palette-form.tsx #: app/login/components/color-palettes/profile-color-palette-form.tsx msgid "controls.custom-color-palettes.type" msgstr "Selezionare un tipo di tavolozza di colori" @@ -1633,6 +1797,7 @@ msgstr "Seleziona una dimensione..." msgid "controls.select.measure" msgstr "Seleziona una misura" +#: app/configurator/components/chart-options-selector/show-dots-field.tsx #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size" msgstr "Selezionare una taglia" @@ -1666,6 +1831,7 @@ msgstr "Seleziona il set di dati con dimensioni condivise" msgid "controls.dimensionvalue.select" msgstr "Seleziona filtro" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.line-type" msgstr "Selezionare il tipo di linea" @@ -1674,6 +1840,7 @@ msgstr "Selezionare il tipo di linea" msgid "controls.filter.select.none" msgstr "Deseleziona tutto" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values.symbol-type" msgstr "Seleziona il tipo di simbolo" @@ -1822,6 +1989,10 @@ msgstr "Esci" msgid "controls.size" msgstr "Grandezza" +#: app/components/skip-link.tsx +msgid "skip-link.main-content" +msgstr "Vai al contenuto principale" + #: app/configurator/components/chart-options-selector/show-dots-field.tsx msgid "controls.section.dots-size.small" msgstr "Piccolo" @@ -1855,6 +2026,11 @@ msgstr "Ordinamento" msgid "controls.sorting.byTotalSize.disabled-by-animation" msgstr "L'ordinamento per dimensione totale è disabilitato durante l'animazione." +#: app/configurator/components/chart-options-selector/sorting-field.tsx +#: app/configurator/table/table-chart-sorting-options.tsx +msgid "controls.sorting.order" +msgstr "Ordine di ordinamento" + #: app/components/dataset-metadata.tsx msgid "dataset.metadata.source" msgstr "Fonte" @@ -1905,6 +2081,7 @@ msgstr "Rimanete informati" msgid "controls.section.animation.type.stepped" msgstr "A gradini" +#: app/configurator/components/chart-annotations/chart-annotations-selector.tsx #: app/configurator/components/chart-annotations/chart-annotations-selector.tsx msgid "controls.annotations.highlight.section.style.title" msgstr "Stile" @@ -1953,6 +2130,7 @@ msgstr "Alto" msgid "controls.tableSettings.targetComponentId" msgstr "Colonna di destinazione" +#: app/configurator/components/chart-options-selector/limits-field.tsx #: app/configurator/components/chart-options-selector/limits-field.tsx msgid "controls.section.targets-and-limit-values" msgstr "Obiettivi e valori limite" @@ -1975,6 +2153,10 @@ msgstr "Colore del testo" msgid "controls.section.block-options.block.text" msgstr "Oggetto di testo" +#: app/components/mdx-editor/block-type-menu.tsx +msgid "mdx-editor.toolbar.block-type" +msgstr "Stile del testo" + #: app/configurator/table/table-chart-options.tsx msgid "controls.select.columnStyle.textStyle" msgstr "Stile del testo" @@ -2047,6 +2229,8 @@ msgstr "Titolo" msgid "controls.section.description" msgstr "Titolo e descrizione" +#: app/charts/shared/brush/index.tsx +#: app/components/dashboard-interactive-filters.tsx #: app/configurator/components/filters.tsx #: app/configurator/components/layout-configurator.tsx #: app/configurator/components/layout-configurator.tsx @@ -2073,6 +2257,10 @@ msgstr "Tutorials" msgid "login.profile.my-visualizations.chart-type" msgstr "Tipo" +#: app/components/mdx-editor/bold-italic-underline-toggles.tsx +msgid "mdx-editor.toolbar.underline" +msgstr "Sottolineato" + #: app/configurator/components/add-dataset-drawer/caution-alert.tsx msgid "dataset.search.caution.acknowledge" msgstr "Capito, procederò con cautela." @@ -2097,6 +2285,10 @@ msgstr "Aggiornare la tavolozza dei colori" msgid "button.update" msgstr "Aggiornare questa visualizzazione" +#: app/components/mdx-editor/link-dialog.tsx +msgid "mdx-editor.link-dialog.url" +msgstr "URL" + #: app/components/mdx-editor/link-dialog.tsx msgid "mdx-editor.link-dialog.url-placeholder" msgstr "URL..." @@ -2214,3 +2406,11 @@ msgstr "Z → A" #: app/configurator/components/field-i18n.ts msgid "controls.imputation.type.zeros" msgstr "Zeri" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-in" +msgstr "Ingrandisci" + +#: app/charts/map/map.tsx +msgid "chart.map.zoom-out" +msgstr "Riduci" diff --git a/app/pages/_app.tsx b/app/pages/_app.tsx index 1adacf70bd..1c56612511 100644 --- a/app/pages/_app.tsx +++ b/app/pages/_app.tsx @@ -91,10 +91,10 @@ export default function App({ - {shouldShowGQLDebug ? : null} + {shouldShowGQLDebug ? : null} diff --git a/e2e/fixtures/column-two-charts-chart-config.json b/e2e/fixtures/column-two-charts-chart-config.json new file mode 100644 index 0000000000..6e2f568556 --- /dev/null +++ b/e2e/fixtures/column-two-charts-chart-config.json @@ -0,0 +1,232 @@ +{ + "state": "CONFIGURING_CHART", + "layout": { + "type": "tab", + "meta": { + "label": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "title": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "description": { + "de": "", + "en": "", + "fr": "", + "it": "" + } + }, + "blocks": [ + { + "type": "chart", + "key": "chart1", + "initialized": true + }, + { + "type": "chart", + "key": "chart2", + "initialized": true + } + ] + }, + "version": "4.2.0", + "dataSource": { + "url": "https://cached.lindas.admin.ch/query", + "type": "sparql" + }, + "chartConfigs": [ + { + "key": "chart1", + "meta": { + "label": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "title": { + "de": "", + "en": "Column with filters", + "fr": "", + "it": "" + }, + "description": { + "de": "", + "en": "", + "fr": "", + "it": "" + } + }, + "cubes": [ + { + "iri": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/10", + "filters": { + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton": { + "type": "single", + "value": "https://ld.admin.ch/canton/1" + }, + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr": { + "to": "VISUALIZE_MOST_RECENT_VALUE", + "from": "2015", + "type": "range" + } + } + } + ], + "fields": { + "x": { + "sorting": { + "sortingType": "byAuto", + "sortingOrder": "asc" + }, + "componentId": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr" + }, + "y": { + "componentId": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/AnzahlAnlagen" + }, + "color": { + "type": "single", + "color": "#006699", + "paletteId": "category10" + } + }, + "version": "4.1.0", + "chartType": "column", + "interactiveFiltersConfig": { + "legend": { + "active": false, + "componentId": "" + }, + "timeRange": { + "active": true, + "presets": { + "to": "VISUALIZE_MOST_RECENT_VALUE", + "from": "2015", + "type": "range" + }, + "componentId": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr" + }, + "calculation": { + "type": "identity", + "active": false + }, + "dataFilters": { + "active": true, + "componentIds": [ + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton" + ], + "defaultValueOverrides": {}, + "filterTypes": {}, + "defaultOpen": true + } + } + }, + { + "key": "chart2", + "meta": { + "label": { + "de": "", + "en": "", + "fr": "", + "it": "" + }, + "title": { + "de": "", + "en": "Column with filters", + "fr": "", + "it": "" + }, + "description": { + "de": "", + "en": "", + "fr": "", + "it": "" + } + }, + "cubes": [ + { + "iri": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/10", + "filters": { + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton": { + "type": "single", + "value": "https://ld.admin.ch/canton/1" + }, + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr": { + "to": "VISUALIZE_MOST_RECENT_VALUE", + "from": "2015", + "type": "range" + } + } + } + ], + "fields": { + "x": { + "sorting": { + "sortingType": "byAuto", + "sortingOrder": "asc" + }, + "componentId": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr" + }, + "y": { + "componentId": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/AnzahlAnlagen" + }, + "color": { + "type": "single", + "color": "#006699", + "paletteId": "category10" + } + }, + "version": "4.1.0", + "chartType": "column", + "interactiveFiltersConfig": { + "legend": { + "active": false, + "componentId": "" + }, + "timeRange": { + "active": true, + "presets": { + "to": "VISUALIZE_MOST_RECENT_VALUE", + "from": "2015", + "type": "range" + }, + "componentId": "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Jahr" + }, + "calculation": { + "type": "identity", + "active": false + }, + "dataFilters": { + "active": true, + "componentIds": [ + "https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen(VISUALIZE.ADMIN_COMPONENT_ID_SEPARATOR)https://energy.ld.admin.ch/sfoe/bfe_ogd84_einmalverguetung_fuer_photovoltaikanlagen/Kanton" + ], + "defaultValueOverrides": {}, + "filterTypes": {}, + "defaultOpen": true + } + } + } + ], + "activeChartKey": "chart1", + "dashboardFilters": { + "timeRange": { + "active": false, + "presets": { + "to": "", + "from": "" + }, + "timeUnit": "" + }, + "dataFilters": { + "filters": {}, + "componentIds": [] + } + } +} diff --git a/e2e/keyboard-widgets.spec.ts b/e2e/keyboard-widgets.spec.ts new file mode 100644 index 0000000000..9f2a1ef852 --- /dev/null +++ b/e2e/keyboard-widgets.spec.ts @@ -0,0 +1,217 @@ +import { Page } from "@playwright/test"; + +import { loadChartInLocalStorage } from "./charts-utils"; +import { setup } from "./common"; +import columnTwoCharts from "./fixtures/column-two-charts-chart-config.json"; + +const { expect, test } = setup(); + +const openEditor = async ({ + page, + activeField, +}: { + page: Page; + activeField?: string; +}) => { + const key = `keyboard-${activeField ?? "main"}`; + const config = structuredClone(columnTwoCharts); + config.chartConfigs[0].activeField = activeField; + await loadChartInLocalStorage(page, key, config); + await page.goto(`/en/create/${key}`); + await page.waitForSelector('[data-chart-loaded="true"]', { timeout: 30_000 }); +}; + +const focused = (page: Page) => page.locator(":focus"); + +test.describe("Keyboard operability", () => { + test("chart actions menu opens on its first item and activates items", async ({ + page, + }) => { + await openEditor({ page }); + await page.getByTestId("chart-more-button").first().focus(); + await page.keyboard.press("Enter"); + + await expect(focused(page)).toHaveAttribute("role", "menuitem"); + await expect(focused(page)).toHaveText("Duplicate"); + + await page.keyboard.press("ArrowDown"); + await expect(focused(page)).toHaveText("Table view"); + + await page.keyboard.press("Enter"); + await expect(page.locator("table").first()).toBeVisible(); + }); + + test("delete confirmation from a menu is reachable by Tab", async ({ + page, + }) => { + await openEditor({ page }); + await page + .getByTestId("chart-selection-tab") + .first() + .getByRole("button", { name: "More actions", exact: true }) + .focus(); + await page.keyboard.press("Enter"); + await page.keyboard.press("End"); + await expect(focused(page)).toHaveText("Delete"); + await page.keyboard.press("Enter"); + + const dialog = page.getByRole("dialog", { name: "Delete chart?" }); + await expect(dialog).toBeVisible(); + await expect(page.getByRole("menu")).toHaveCount(0); + + await page.keyboard.press("Tab"); + await expect(focused(page)).toHaveText("No"); + await page.keyboard.press("Tab"); + await expect(focused(page)).toHaveText("Yes"); + + await page.keyboard.press("Enter"); + await expect(page.getByTestId("chart-selection-tab")).toHaveCount(1); + }); + + test("add chart popover can add a chart by keyboard", async ({ page }) => { + await openEditor({ page }); + await page.getByRole("button", { name: "Add chart" }).focus(); + await page.keyboard.press("Enter"); + + const popover = page.getByRole("dialog", { name: "Add chart" }); + await expect(popover).toBeVisible(); + await expect(focused(page)).toHaveText("Columns"); + + await page.keyboard.press("Tab"); + await expect(focused(page)).toHaveText("Bars"); + await page.keyboard.press("Enter"); + + await expect(page.getByTestId("chart-selection-tab")).toHaveCount(3); + }); + + test("Tab leaves the rich text editor without inserting a tab", async ({ + page, + }) => { + await openEditor({ page, activeField: "title" }); + const editor = page.getByRole("textbox", { name: "English" }); + await editor.focus(); + await page.keyboard.press("End"); + await page.keyboard.press("Tab"); + + await expect(editor).not.toBeFocused(); + expect(await editor.evaluate((e) => e.textContent)).toBe( + "Column with filters" + ); + + await page.keyboard.press("Shift+Tab"); + await expect(editor).toBeFocused(); + + await page.keyboard.press("Shift+Tab"); + await expect(focused(page)).toHaveAttribute("aria-label", "Bold"); + await page.keyboard.press("ArrowRight"); + await expect(focused(page)).toHaveAttribute("aria-label", "Italic"); + }); + + test("color palette select works with the keyboard", async ({ page }) => { + await openEditor({ page, activeField: "segment" }); + const drawer = page.getByTestId("panel-drawer"); + await drawer.getByRole("combobox").first().focus(); + await page.keyboard.press("Enter"); + await page.getByRole("option", { name: "Kanton" }).focus(); + await page.keyboard.press("Enter"); + + const palette = drawer.getByRole("combobox", { name: "Color palette" }); + await palette.focus(); + await page.keyboard.press("Enter"); + await expect(focused(page)).toHaveAttribute("role", "option"); + await expect( + page.getByRole("option", { name: /color palettes/ }) + ).toHaveCount(0); + await page.keyboard.press("ArrowDown"); + const next = await focused(page).getAttribute("data-value"); + await page.keyboard.press("Enter"); + + await expect(palette).toBeFocused(); + await expect + .poll(() => + page.evaluate( + () => + JSON.parse( + localStorage.getItem( + "vizualize-configurator-state:keyboard-segment" + ) ?? "{}" + ).chartConfigs[0].fields.color.paletteId + ) + ) + .toBe(next); + }); + + test("color picker is operable by keyboard", async ({ page }) => { + await openEditor({ page, activeField: "segment" }); + const drawer = page.getByTestId("panel-drawer"); + await drawer.getByRole("combobox").first().focus(); + await page.keyboard.press("Enter"); + await page.getByRole("option", { name: "Kanton" }).focus(); + await page.keyboard.press("Enter"); + + const opener = drawer.getByRole("button", { + name: "Open color picker: Zurich", + }); + await opener.focus(); + await page.keyboard.press("Enter"); + + const dialog = page.getByRole("dialog", { name: "Color picker" }); + await expect(dialog).toBeVisible(); + const saturation = dialog.getByRole("slider", { + name: "Saturation and brightness", + }); + await expect(saturation).toBeFocused(); + + const hex = dialog.getByRole("textbox", { name: "Hex color" }); + const before = await hex.inputValue(); + await page.keyboard.press("Shift+ArrowRight"); + await expect(hex).not.toHaveValue(before); + + await page.keyboard.press("Escape"); + await expect(dialog).toBeHidden(); + await expect(opener).toBeFocused(); + }); + + test("Escape closes the edit filters drawer", async ({ page }) => { + await openEditor({ page, activeField: "segment" }); + const drawer = page.getByTestId("panel-drawer"); + await drawer.getByRole("combobox").first().focus(); + await page.keyboard.press("Enter"); + await page.getByRole("option", { name: "Kanton" }).focus(); + await page.keyboard.press("Enter"); + + const editFilters = drawer.getByRole("button", { name: "Edit filters" }); + await editFilters.focus(); + await page.keyboard.press("Enter"); + await expect(page.getByTestId("edition-filters-drawer")).toBeVisible(); + + await page.keyboard.press("Escape"); + await expect(page.getByTestId("edition-filters-drawer")).toBeHidden(); + await expect(editFilters).toBeFocused(); + }); + + test("time range brush handles move with arrow keys", async ({ page }) => { + await openEditor({ page }); + const from = page.locator(".handle--w").first(); + await from.focus(); + const before = await from.getAttribute("aria-valuetext"); + + await page.keyboard.press("ArrowRight"); + await expect(from).not.toHaveAttribute("aria-valuetext", before!); + + await page.keyboard.press("ArrowLeft"); + await expect(from).toHaveAttribute("aria-valuetext", before!); + }); + + test("skip link moves focus to the main content", async ({ page }) => { + await page.goto("/en"); + await page + .getByRole("link", { name: "Skip to main content" }) + .waitFor({ state: "attached" }); + await page.keyboard.press("Tab"); + await expect(focused(page)).toHaveText("Skip to main content"); + + await page.keyboard.press("Enter"); + await expect(page.locator("main")).toBeFocused(); + }); +}); From 511bcc6b6816f1032780f5c49dfd9657e6de9a0b Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 13 Nov 2026 10:04:05 +0100 Subject: [PATCH 05/11] fix: Toolbar active contrast, link dialog focus and group labels from review --- app/components/chart-selection-tabs.tsx | 11 +++++++ app/components/form.spec.tsx | 18 +++++++++++ app/components/form.tsx | 19 +++++++----- app/components/mdx-editor/block-type-menu.tsx | 1 - app/components/mdx-editor/common.tsx | 30 +++++++++++-------- app/components/mdx-editor/link-dialog.tsx | 2 +- app/components/skip-link.tsx | 21 +++++++------ .../chart-annotations-selector.tsx | 9 ++---- .../components/chart-configurator.tsx | 11 +++++-- .../chart-controls/color-palette.tsx | 1 + .../animation-field.tsx | 27 ++++++++--------- .../calculation-field.tsx | 15 +++++----- .../color-component-field.tsx | 11 ++++--- .../chart-options-selector/layout-field.tsx | 11 ++----- .../chart-options-selector/limits-field.tsx | 28 ++++++++--------- .../show-dots-field.tsx | 9 ++---- app/configurator/components/field.tsx | 3 ++ .../table/table-chart-sorting-options.tsx | 6 +++- app/locales/de/messages.po | 15 +++------- app/locales/en/messages.po | 15 +++------- app/locales/fr/messages.po | 15 +++------- app/locales/it/messages.po | 15 +++------- .../profile-color-palette-form.tsx | 15 +++++----- app/utils/observables.ts | 2 -- 24 files changed, 160 insertions(+), 150 deletions(-) diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index f205a05d2b..560b2e3a68 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -168,6 +168,11 @@ const TabsEditable = (props: TabsEditableProps) => { <> 1} @@ -419,6 +424,7 @@ const useTabsInnerStyles = makeStyles((theme) => ({ const TabsInner = ({ data, + menuChartKey, addable, editable, draggable, @@ -427,6 +433,7 @@ const TabsInner = ({ onChartSwitch, }: { data: TabDatum[]; + menuChartKey?: string; addable: boolean; editable: boolean; draggable: boolean; @@ -512,6 +519,7 @@ const TabsInner = ({ editable={editable} draggable={draggable} active={d.active} + menuOpen={d.key === menuChartKey} label={d.label} dragging={snapshot.isDragging} onChevronDownClick={(e) => { @@ -619,6 +627,7 @@ const TabContent = (props: { editable?: boolean; draggable?: boolean; active?: boolean; + menuOpen?: boolean; dragging?: boolean; label?: string; onChevronDownClick?: ( @@ -633,6 +642,7 @@ const TabContent = (props: { editable, draggable, active, + menuOpen, label, dragging, onChevronDownClick, @@ -687,6 +697,7 @@ const TabContent = (props: { : t({ id: "menu.more-actions", message: "More actions" }) } aria-haspopup="menu" + aria-expanded={!!menuOpen} onClick={(e) => onChevronDownClick?.(e, chartKey)} className={classes.editIconWrapper} > diff --git a/app/components/form.spec.tsx b/app/components/form.spec.tsx index e23839acf0..52d1f77d80 100644 --- a/app/components/form.spec.tsx +++ b/app/components/form.spec.tsx @@ -30,6 +30,24 @@ describe("RadioGroup", () => { }); }); +describe("RadioGroup with a visible caption", () => { + it("is labelled by the caption and shares one name", () => { + render( + <> +

Palette type

+ + + + + + ); + + const group = screen.getByRole("radiogroup", { name: "Palette type" }); + const names = [...group.querySelectorAll("input")].map((i) => i.name); + expect(new Set(names)).toEqual(new Set(["type"])); + }); +}); + describe("Input", () => { it("links the error message to the invalid input", () => { render( diff --git a/app/components/form.tsx b/app/components/form.tsx index e370ec4ed8..ac21008ff4 100644 --- a/app/components/form.tsx +++ b/app/components/form.tsx @@ -133,13 +133,21 @@ export const Label = ({ export const RadioGroup = ({ label, + labelId, children, }: { - label: string; children: ReactNode; -}) => { +} & ( + | { label: string; labelId?: never } + | { labelId: string; label?: never } +)) => { return ( - + {children} ); @@ -748,11 +756,6 @@ const useMarkdownInputStyles = makeStyles((theme) => ({ border: `1px solid ${theme.palette.monochrome[500]}`, }, - "&:focus-visible": { - outline: `2px solid var(--focus-outline-color, ${theme.palette.primary.main})`, - outlineOffset: 2, - }, - "& *": { margin: "1em 0", lineHeight: 1.2, diff --git a/app/components/mdx-editor/block-type-menu.tsx b/app/components/mdx-editor/block-type-menu.tsx index 178215016b..214ea279db 100644 --- a/app/components/mdx-editor/block-type-menu.tsx +++ b/app/components/mdx-editor/block-type-menu.tsx @@ -92,7 +92,6 @@ export const BlockTypeMenu = () => { onClick={(e) => { setAnchor(e.currentTarget); }} - sx={{ backgroundColor: anchor ? "grey.300" : "transparent" }} > diff --git a/app/components/mdx-editor/common.tsx b/app/components/mdx-editor/common.tsx index ad970f61b7..c26d2c9de4 100644 --- a/app/components/mdx-editor/common.tsx +++ b/app/components/mdx-editor/common.tsx @@ -1,18 +1,24 @@ -import { IconButton, IconButtonProps, SxProps, Theme } from "@mui/material"; +import { IconButton, IconButtonProps } from "@mui/material"; import { forwardRef } from "react"; -const ACTIVE_SX: SxProps = { - backgroundColor: "monochrome.800", - color: "white", +const ACTIVE_SX = { + '&[aria-pressed="true"], &[aria-expanded="true"]': { + backgroundColor: "monochrome.800", + color: "white", - "&:hover": { - backgroundColor: "monochrome.700", - }, + "&:hover": { + backgroundColor: "monochrome.700", + }, + + "&& svg": { + color: "inherit", + }, - "@media (forced-colors: active)": { - forcedColorAdjust: "none", - backgroundColor: "Highlight", - color: "HighlightText", + "@media (forced-colors: active)": { + forcedColorAdjust: "none", + backgroundColor: "Highlight", + color: "HighlightText", + }, }, }; @@ -26,7 +32,7 @@ export const ToolbarIconButton = forwardRef< aria-label={label} aria-pressed={active} style={{ ...style, padding: "2px", borderRadius: 4 }} - sx={[!!active && ACTIVE_SX, ...(Array.isArray(sx) ? sx : [sx])]} + sx={[ACTIVE_SX, ...(Array.isArray(sx) ? sx : [sx])]} {...rest} /> ); diff --git a/app/components/mdx-editor/link-dialog.tsx b/app/components/mdx-editor/link-dialog.tsx index a26de222e1..35e2461d78 100644 --- a/app/components/mdx-editor/link-dialog.tsx +++ b/app/components/mdx-editor/link-dialog.tsx @@ -529,7 +529,7 @@ const LinkDialog = () => { {maybeDelayedState.type === "edit" && ( - +
({ root: { - position: "absolute", - top: theme.spacing(2), - left: theme.spacing(2), - zIndex: theme.zIndex.tooltip + 1, - padding: theme.spacing(2, 4), - backgroundColor: theme.palette.background.paper, - ...theme.typography.body2, - transform: "translateY(-200%)", + ...visuallyHidden, "&:focus": { - transform: "none", + position: "static", + alignSelf: "flex-start", + width: "auto", + height: "auto", + margin: theme.spacing(2), + padding: theme.spacing(1, 2), + overflow: "visible", + clip: "auto", + whiteSpace: "normal", + ...theme.typography.body2, }, }, })); diff --git a/app/configurator/components/chart-annotations/chart-annotations-selector.tsx b/app/configurator/components/chart-annotations/chart-annotations-selector.tsx index 802e0042a9..1d5992a1fb 100644 --- a/app/configurator/components/chart-annotations/chart-annotations-selector.tsx +++ b/app/configurator/components/chart-annotations/chart-annotations-selector.tsx @@ -190,18 +190,13 @@ export const ChartAnnotationsSelector = () => { {isSegmented ? null : ( - + Style - + d.cubeIri === rawDimension.cubeIri ) ?? rawDimension; const { label } = dimension; + const id = `select-single-filter-${index}`; const sharedProps = { dimension, label: disableLabel ? null : ( <> - + {`${index + 1}. ${dimension.label}`} @@ -195,7 +196,7 @@ export const DataFilterSelectGeneric = ({ ), sideControls, - id: `select-single-filter-${index}`, + id, disabled: fetching || disabled, optional: !dimension.isKeyDimension, loading: fetching, @@ -214,7 +215,11 @@ export const DataFilterSelectGeneric = ({ ); } else { return ( - + ); } }; diff --git a/app/configurator/components/chart-controls/color-palette.tsx b/app/configurator/components/chart-controls/color-palette.tsx index ea841a4da6..6a9a2a245c 100644 --- a/app/configurator/components/chart-controls/color-palette.tsx +++ b/app/configurator/components/chart-controls/color-palette.tsx @@ -341,6 +341,7 @@ const PaletteGroupHeader = ({ children }: { children: ReactNode }) => { { {field.showPlayButton && ( <> - + Animation Duration - + {[10, 30, 60].map((d) => ( { - + Animation Type @@ -79,12 +83,7 @@ export const AnimationField = ({ field }: { field: AnimationFieldType }) => { - + { return ( - + Chart mode - + - + Scale type - + - + Column layout - + {values.map((d) => ( {limit.type === "single" && supportsLimitSymbols ? (
- + Select symbol type - + - + Select line type - + - + Select a Size - + - + Select a color palette type - + {types.map((type) => { return ( Date: Fri, 13 Nov 2026 10:45:40 +0100 Subject: [PATCH 06/11] fix: Label semantics, link dialog focus and popup states from review --- app/components/chart-selection-tabs.tsx | 11 ++++++++++- app/components/form.spec.tsx | 18 ++++++++++++++++++ app/components/form.tsx | 2 +- app/components/header.tsx | 2 ++ app/components/layout.tsx | 11 ++--------- app/components/mdx-editor/link-dialog.tsx | 11 ++++++++++- app/components/menu-action-item.tsx | 14 +++++++++++++- app/components/skip-link.tsx | 2 +- .../components/chart-configurator.tsx | 4 +++- .../components/layout-configurator.tsx | 8 +++++++- app/login/components/login-menu.tsx | 4 ++++ 11 files changed, 71 insertions(+), 16 deletions(-) diff --git a/app/components/chart-selection-tabs.tsx b/app/components/chart-selection-tabs.tsx index 560b2e3a68..540e9d81b5 100644 --- a/app/components/chart-selection-tabs.tsx +++ b/app/components/chart-selection-tabs.tsx @@ -168,6 +168,9 @@ const TabsEditable = (props: TabsEditableProps) => { <> ((theme) => ({ const TabsInner = ({ data, + addPopoverOpen, menuChartKey, addable, editable, @@ -433,6 +437,7 @@ const TabsInner = ({ onChartSwitch, }: { data: TabDatum[]; + addPopoverOpen?: boolean; menuChartKey?: string; addable: boolean; editable: boolean; @@ -545,7 +550,11 @@ const TabsInner = ({ {addable && ( - + Add chart )} diff --git a/app/components/form.spec.tsx b/app/components/form.spec.tsx index 52d1f77d80..12c089d74e 100644 --- a/app/components/form.spec.tsx +++ b/app/components/form.spec.tsx @@ -90,4 +90,22 @@ describe("Select", () => { expect(screen.getByRole("combobox", { name: /^Sort by/ })).toBeTruthy(); }); + + it("keeps the names of buttons inside its label", () => { + render( + { state$ ); const delayedState = useTimedPrevious(state, TIMEOUT_MS); - const maybeDelayedState = state.type === "inactive" ? delayedState : state; + const maybeDelayedState = + state.type === "inactive" && delayedState.type === "preview" + ? delayedState + : state; const publishWindowChange = usePublisher(onWindowChange$); const updateLink = usePublisher(updateLink$); const cancelLinkEdit = usePublisher(cancelLinkEdit$); diff --git a/e2e/keyboard-widgets.spec.ts b/e2e/keyboard-widgets.spec.ts index 9f2a1ef852..81a32c490b 100644 --- a/e2e/keyboard-widgets.spec.ts +++ b/e2e/keyboard-widgets.spec.ts @@ -9,13 +9,16 @@ const { expect, test } = setup(); const openEditor = async ({ page, activeField, + description = "", }: { page: Page; activeField?: string; + description?: string; }) => { const key = `keyboard-${activeField ?? "main"}`; const config = structuredClone(columnTwoCharts); config.chartConfigs[0].activeField = activeField; + config.chartConfigs[0].meta.description.en = description; await loadChartInLocalStorage(page, key, config); await page.goto(`/en/create/${key}`); await page.waitForSelector('[data-chart-loaded="true"]', { timeout: 30_000 }); @@ -107,6 +110,42 @@ test.describe("Keyboard operability", () => { await expect(focused(page)).toHaveAttribute("aria-label", "Italic"); }); + test("saving a link returns focus to the editor", async ({ page }) => { + await page.setViewportSize({ width: 1440, height: 900 }); + await openEditor({ + page, + activeField: "description", + description: "See docs here", + }); + const editor = page.getByRole("textbox", { name: "English" }); + await editor.focus(); + await page.keyboard.press("Home"); + for (let i = 0; i < 4; i++) { + await page.keyboard.press("ArrowRight"); + } + for (let i = 0; i < 4; i++) { + await page.keyboard.press("Shift+ArrowRight"); + } + await page.waitForTimeout(300); + await page.keyboard.press("Shift+Tab"); + await page.keyboard.press("End"); + await expect(focused(page)).toHaveAttribute("aria-label", "Link"); + await page.keyboard.press("Enter"); + + const dialog = page.getByRole("dialog", { name: "Edit link" }); + await expect(dialog).toBeVisible(); + await page.keyboard.press("Tab"); + await page.keyboard.type("https://example.com"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await expect(focused(page)).toHaveText("Save"); + await page.keyboard.press("Enter"); + + await expect(editor.getByRole("link", { name: "docs" })).toBeVisible(); + await page.waitForTimeout(1000); + await expect(editor).toBeFocused(); + }); + test("color palette select works with the keyboard", async ({ page }) => { await openEditor({ page, activeField: "segment" }); const drawer = page.getByTestId("panel-drawer"); From 7e7faf29a4208cbb3f9706e3fb737e54c7b7b9a2 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 13 Nov 2026 12:06:52 +0100 Subject: [PATCH 08/11] fix: Tree row names and layer search focus and results message from review --- app/charts/map/wms-wmts-selector.tsx | 17 +++++++++++++---- app/configurator/components/filters.tsx | 1 + app/locales/de/messages.po | 2 +- app/locales/en/messages.po | 2 +- app/locales/fr/messages.po | 2 +- app/locales/it/messages.po | 2 +- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/charts/map/wms-wmts-selector.tsx b/app/charts/map/wms-wmts-selector.tsx index 21f7fdd901..fa1185f56e 100644 --- a/app/charts/map/wms-wmts-selector.tsx +++ b/app/charts/map/wms-wmts-selector.tsx @@ -1,4 +1,4 @@ -import { t, Trans } from "@lingui/macro"; +import { plural, t, Trans } from "@lingui/macro"; import { TreeItem, TreeView } from "@mui/lab"; import { Box, @@ -14,7 +14,7 @@ import { import { makeStyles } from "@mui/styles"; import sortBy from "lodash/sortBy"; import uniqBy from "lodash/uniqBy"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; import createStore from "zustand"; import { RemoteLayer } from "@/charts/map/types"; @@ -304,6 +304,7 @@ export const WMTSSelector = ({ onChangeInputValue: setInputValue, }); + const searchInputRef = useRef(null); const resultsCount = useMemo(() => { let count = 0; visitHierarchy(unsortedFilteredOptions, () => { @@ -398,6 +399,7 @@ export const WMTSSelector = ({ } placeholder={t({ @@ -418,7 +420,10 @@ export const WMTSSelector = ({ id: "controls.search.clear", message: "Clear search field", })} - onClick={handleClickResetInput} + onClick={() => { + handleClickResetInput(); + searchInputRef.current?.focus(); + }} > @@ -452,7 +457,11 @@ export const WMTSSelector = ({ inputValue && status !== "fetching" ? t({ id: "wmts.search-results", - message: `${resultsCount} results`, + message: plural(resultsCount, { + 0: "No results", + one: "# result", + other: "# results", + }), }) : "" } diff --git a/app/configurator/components/filters.tsx b/app/configurator/components/filters.tsx index b91bbd544e..4b0bd73bb2 100644 --- a/app/configurator/components/filters.tsx +++ b/app/configurator/components/filters.tsx @@ -765,6 +765,7 @@ const TreeAccordion = ({ slotProps={{ transition: { unmountOnExit: true } }} > { e.stopPropagation(); diff --git a/app/locales/de/messages.po b/app/locales/de/messages.po index fa6dbff4ed..fd34ba5a77 100644 --- a/app/locales/de/messages.po +++ b/app/locales/de/messages.po @@ -103,7 +103,7 @@ msgstr "{0} von {1}" #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-results" -msgstr "{resultsCount} Ergebnisse" +msgstr "{resultsCount, plural, =0 {Keine Ergebnisse} one {# Ergebnis} other {# Ergebnisse}}" #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" diff --git a/app/locales/en/messages.po b/app/locales/en/messages.po index 73d1e314e0..3943c882c8 100644 --- a/app/locales/en/messages.po +++ b/app/locales/en/messages.po @@ -103,7 +103,7 @@ msgstr "{0} of {1}" #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-results" -msgstr "{resultsCount} results" +msgstr "{resultsCount, plural, =0 {No results} one {# result} other {# results}}" #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" diff --git a/app/locales/fr/messages.po b/app/locales/fr/messages.po index e2517e7722..ba75f46357 100644 --- a/app/locales/fr/messages.po +++ b/app/locales/fr/messages.po @@ -103,7 +103,7 @@ msgstr "{0} sur {1}" #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-results" -msgstr "{resultsCount} résultats" +msgstr "{resultsCount, plural, =0 {Aucun résultat} one {# résultat} other {# résultats}}" #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" diff --git a/app/locales/it/messages.po b/app/locales/it/messages.po index 1f49550cdf..3264ffb761 100644 --- a/app/locales/it/messages.po +++ b/app/locales/it/messages.po @@ -103,7 +103,7 @@ msgstr "{0} di {1}" #: app/charts/map/wms-wmts-selector.tsx msgid "wmts.search-results" -msgstr "{resultsCount} risultati" +msgstr "{resultsCount, plural, =0 {Nessun risultato} one {# risultato} other {# risultati}}" #: app/configurator/components/field-i18n.ts msgid "controls.sorting.byMeasure.ascending" From 3c1d6599c69be80923993bc23518e113ee124ef1 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 13 Nov 2026 12:10:54 +0100 Subject: [PATCH 09/11] fix: Name the map color ramp select and its options --- app/components/select-group-header.tsx | 18 ++++++++++++++ .../chart-controls/color-palette.tsx | 7 +++--- .../components/chart-controls/color-ramp.tsx | 24 +++++++++++-------- 3 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 app/components/select-group-header.tsx diff --git a/app/components/select-group-header.tsx b/app/components/select-group-header.tsx new file mode 100644 index 0000000000..3b4f88f4f9 --- /dev/null +++ b/app/components/select-group-header.tsx @@ -0,0 +1,18 @@ +import { ListSubheader, ListSubheaderProps } from "@mui/material"; +import { ReactNode } from "react"; + +export const SelectGroupHeader = ({ + children, + sx, +}: { + children: ReactNode; + sx?: ListSubheaderProps["sx"]; +}) => { + return ( + + {children} + + ); +}; + +SelectGroupHeader.muiSkipListHighlight = true; diff --git a/app/configurator/components/chart-controls/color-palette.tsx b/app/configurator/components/chart-controls/color-palette.tsx index 6a9a2a245c..09d4d60409 100644 --- a/app/configurator/components/chart-controls/color-palette.tsx +++ b/app/configurator/components/chart-controls/color-palette.tsx @@ -2,7 +2,6 @@ import { Trans } from "@lingui/macro"; import { Box, Button, - ListSubheader, MenuItem, MenuItemProps, Select, @@ -17,6 +16,7 @@ import { EncodingFieldType } from "@/charts/chart-config-ui-options"; import { hasDimensionColors } from "@/charts/shared/colors"; import { Flex } from "@/components/flex"; import { Label, selectMenuProps } from "@/components/form"; +import { SelectGroupHeader } from "@/components/select-group-header"; import { getChartConfig } from "@/config-utils"; import { ConfiguratorStateConfiguringChart, @@ -338,8 +338,7 @@ export const ColorPalette = ({ const PaletteGroupHeader = ({ children }: { children: ReactNode }) => { return ( - { }} > {children} - + ); }; diff --git a/app/configurator/components/chart-controls/color-ramp.tsx b/app/configurator/components/chart-controls/color-ramp.tsx index 82ffd94be4..aa029e0e08 100644 --- a/app/configurator/components/chart-controls/color-ramp.tsx +++ b/app/configurator/components/chart-controls/color-ramp.tsx @@ -2,12 +2,12 @@ import { Trans } from "@lingui/macro"; import { Box, Button, - ListSubheader, MenuItem, Select, SelectProps, Typography, } from "@mui/material"; +import { useId } from "@reach/auto-id"; import get from "lodash/get"; import { Dispatch, @@ -22,6 +22,7 @@ import { import { EncodingFieldType } from "@/charts/chart-config-ui-options"; import { Label } from "@/components/form"; +import { SelectGroupHeader } from "@/components/select-group-header"; import { getChartConfig } from "@/config-utils"; import { CustomPaletteType, @@ -101,6 +102,7 @@ type ColorRampFieldProps = Omit & { }; export const ColorRampField = (props: ColorRampFieldProps) => { + const labelId = `color-ramp-label-${useId()}`; const { field, path, disabled, nSteps } = props; const locale = useLocale(); const [state, dispatch] = useConfiguratorState(isConfiguring); @@ -186,10 +188,11 @@ export const ColorRampField = (props: ColorRampFieldProps) => { return ( - { } return ( - + <> + + {currentPalette?.label ?? currentCustomPalette?.name} + + + ); }} sx={{ mt: 1, lineHeight: 0 }} @@ -260,7 +267,11 @@ export const ColorRampField = (props: ColorRampFieldProps) => { type PaletteSectionProps = { nSteps?: number; customColorPalettes?: CustomPaletteType[]; - colorPalettes: { value: string; interpolator: (t: number) => string }[]; + colorPalettes: { + value: string; + label: string; + interpolator: (t: number) => string; + }[]; handleAddColorPalette: ( e: MouseEvent ) => void; @@ -362,12 +373,12 @@ const PaletteSection = (props: PaletteSectionProps) => { ), - colorPalettes.map(({ value, interpolator }, i) => ( + colorPalettes.map(({ value, label, interpolator }, i) => ( diff --git a/app/configurator/components/chart-controls/section.tsx b/app/configurator/components/chart-controls/section.tsx index 33a8056aa1..a6a38b7c99 100644 --- a/app/configurator/components/chart-controls/section.tsx +++ b/app/configurator/components/chart-controls/section.tsx @@ -5,23 +5,26 @@ import { Collapse, Skeleton, Theme, + Tooltip, Typography, TypographyProps, useEventCallback, } from "@mui/material"; import { makeStyles } from "@mui/styles"; +import { useId } from "@reach/auto-id"; import clsx from "clsx"; import { ComponentProps, createContext, + FocusEvent, forwardRef, HTMLProps, ReactNode, useContext, useMemo, + useState, } from "react"; -import { MaybeTooltip } from "@/components/maybe-tooltip"; import { useDisclosure } from "@/components/use-disclosure"; import { VisuallyHidden } from "@/components/visually-hidden"; import { isConfiguring, isLayouting } from "@/configurator/configurator-state"; @@ -289,6 +292,8 @@ export const SectionTitle = ({ } }); + const warningId = `section-warning-${useId()}`; + const [warningOpen, setWarningOpen] = useState(false); const { setOpen, isOpen, collapse } = useControlSectionContext(); const classes = useSectionTitleStyles({ disabled, @@ -298,12 +303,17 @@ export const SectionTitle = ({ }); return ( - + <> ) => { + setWarningOpen(e.currentTarget.matches(":focus-visible")); + }} + onBlur={() => setWarningOpen(false)} > {warnMessage && ( - - - + setWarningOpen(true)} + onClose={() => setWarningOpen(false)} + > + + + + )} {collapse ? : null} - + {warnMessage && ( + + {warnMessage} + + )} + ); }; From ad2d251fc2d0bb6434c5f2d5ea616513bcd40629 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 13 Nov 2026 12:48:53 +0100 Subject: [PATCH 11/11] fix: Escape closes only the section warning and localized link URL hint --- app/components/mdx-editor/link-dialog.tsx | 5 +- .../chart-controls/section.spec.tsx | 89 +++++++++++++++++++ .../components/chart-controls/section.tsx | 7 ++ app/locales/de/messages.po | 4 + app/locales/en/messages.po | 4 + app/locales/fr/messages.po | 4 + app/locales/it/messages.po | 4 + e2e/keyboard-widgets.spec.ts | 4 +- 8 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 app/configurator/components/chart-controls/section.spec.tsx diff --git a/app/components/mdx-editor/link-dialog.tsx b/app/components/mdx-editor/link-dialog.tsx index 7682af22a3..93c8844e9e 100644 --- a/app/components/mdx-editor/link-dialog.tsx +++ b/app/components/mdx-editor/link-dialog.tsx @@ -422,7 +422,10 @@ const LinkEditForm = ({ })} inputProps={{ pattern: "https?://.*|www\\..*", - title: "Enter a valid URL starting with https:// or www.", + title: t({ + id: "mdx-editor.link-dialog.url-hint", + message: "Enter a valid URL starting with https:// or www.", + }), }} {...register("url")} /> diff --git a/app/configurator/components/chart-controls/section.spec.tsx b/app/configurator/components/chart-controls/section.spec.tsx new file mode 100644 index 0000000000..9be1d0d3de --- /dev/null +++ b/app/configurator/components/chart-controls/section.spec.tsx @@ -0,0 +1,89 @@ +import { ThemeProvider } from "@mui/material"; +import { + act, + cleanup, + fireEvent, + render, + screen, +} from "@testing-library/react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { + ControlSection, + SectionTitle, +} from "@/configurator/components/chart-controls/section"; +import { theme } from "@/themes/theme"; + +vi.mock("@lingui/macro", () => ({ + t: ({ message }: { message: string }) => message, + defineMessage: ({ message }: { message: string }) => message, +})); + +vi.mock("@/src", () => ({ + useConfiguratorState: () => [{}, vi.fn()], +})); + +beforeEach(() => { + vi.useFakeTimers(); +}); + +afterEach(() => { + cleanup(); + vi.useRealTimers(); +}); + +const renderSection = (onPanelKeyDown = vi.fn()) => { + render( + +
+ + + Title & Description + + +
+
+ ); + + return screen.getByRole("button", { name: "Title & Description" }); +}; + +describe("SectionTitle", () => { + it("exposes its expanded state", () => { + const header = renderSection(); + expect(header.getAttribute("aria-expanded")).toBe("true"); + + fireEvent.click(header); + + expect(header.getAttribute("aria-expanded")).toBe("false"); + }); + + it("describes the header with its warning", () => { + const header = renderSection(); + const description = document.getElementById( + header.getAttribute("aria-describedby") ?? "" + ); + + expect(description?.textContent).toBe("Please add a title."); + }); + + it("closes the warning tooltip with Escape without closing the panel", () => { + const onPanelKeyDown = vi.fn(); + const header = renderSection(onPanelKeyDown); + const icon = header.querySelector("svg")?.closest("p, span")!; + + fireEvent.mouseOver(icon); + act(() => { + vi.advanceTimersByTime(500); + }); + expect(screen.getByRole("tooltip").textContent).toBe("Please add a title."); + + fireEvent.keyDown(header, { key: "Escape" }); + act(() => { + vi.advanceTimersByTime(500); + }); + + expect(onPanelKeyDown).not.toHaveBeenCalled(); + expect(screen.queryByRole("tooltip")).toBeNull(); + }); +}); diff --git a/app/configurator/components/chart-controls/section.tsx b/app/configurator/components/chart-controls/section.tsx index a6a38b7c99..e48acd0cfb 100644 --- a/app/configurator/components/chart-controls/section.tsx +++ b/app/configurator/components/chart-controls/section.tsx @@ -19,6 +19,7 @@ import { FocusEvent, forwardRef, HTMLProps, + KeyboardEvent, ReactNode, useContext, useMemo, @@ -314,6 +315,12 @@ export const SectionTitle = ({ setWarningOpen(e.currentTarget.matches(":focus-visible")); }} onBlur={() => setWarningOpen(false)} + onKeyDown={(e: KeyboardEvent) => { + if (warningOpen && e.key === "Escape") { + e.stopPropagation(); + setWarningOpen(false); + } + }} > { await page.getByRole("option", { name: "Kanton" }).focus(); await page.keyboard.press("Enter"); - const palette = drawer.getByRole("combobox", { name: "Color palette" }); + const palette = drawer.getByRole("combobox", { name: /^Color palette/ }); + await expect(palette).toHaveAccessibleName("Color palette category10"); await palette.focus(); await page.keyboard.press("Enter"); await expect(focused(page)).toHaveAttribute("role", "option"); @@ -166,6 +167,7 @@ test.describe("Keyboard operability", () => { await page.keyboard.press("Enter"); await expect(palette).toBeFocused(); + await expect(palette).toHaveAccessibleName(`Color palette ${next}`); await expect .poll(() => page.evaluate(