Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ You can also check the
- Features
- Add SPARQL endpoints in the OpenTelemetry traces
- Add Sentry integration back
- Allow the definition of language-specific links in table diagrams
- Refactoring
- Incorporate the Swiss-Federal-CI library into this repository.
- Fixes
Expand Down
7 changes: 6 additions & 1 deletion app/charts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,7 +620,12 @@ export const getInitialConfig = (
},
links: {
enabled: false,
baseUrl: "",
baseUrl: {
de: "",
fr: "",
it: "",
en: "",
},
componentId: "",
targetComponentId: "",
},
Expand Down
6 changes: 4 additions & 2 deletions app/charts/table/linked-cell-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ColumnMeta } from "@/charts/table/table-state";
import { TableLinks } from "@/config-types";
import { Observation } from "@/domain/data";
import { Icon } from "@/icons";
import { useLocale } from "@/locales/use-locale";

const useStyles = makeStyles((theme: Theme) => ({
link: {
Expand Down Expand Up @@ -55,13 +56,14 @@ export const LinkedCellWrapper = ({
links: TableLinks;
}) => {
const classes = useStyles();
const locale = useLocale();
const isLinkedColumn =
links.enabled &&
links.baseUrl.trim() !== "" &&
links.baseUrl[locale].trim() !== "" &&
links.componentId.trim() !== "" &&
links.targetComponentId.trim() !== "" &&
getSlugifiedId(links.targetComponentId) === columnMeta.slugifiedId;
const href = getLinkHref(cell, links.baseUrl, links.componentId);
const href = getLinkHref(cell, links.baseUrl[locale], links.componentId);

if (!isLinkedColumn || !href) {
return <>{children}</>;
Expand Down
10 changes: 9 additions & 1 deletion app/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -825,9 +825,17 @@ const TableSettings = t.type({
});
export type TableSettings = t.TypeOf<typeof TableSettings>;

const TableLinksBaseUrl = t.type({
de: t.string,
fr: t.string,
it: t.string,
en: t.string,
});
export type TableLinksBaseUrl = t.TypeOf<typeof TableLinksBaseUrl>;

const TableLinks = t.type({
enabled: t.boolean,
baseUrl: t.string,
baseUrl: TableLinksBaseUrl,
componentId: t.string,
targetComponentId: t.string,
});
Expand Down
7 changes: 6 additions & 1 deletion app/configurator/configurator-state/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,12 @@ export const configJoinedCubes: Partial<
},
links: {
enabled: false,
baseUrl: "",
baseUrl: {
de: "",
fr: "",
it: "",
en: "",
},
componentId: "",
targetComponentId: "",
},
Expand Down
2 changes: 1 addition & 1 deletion app/configurator/configurator-state/reducer.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ describe("deriveFiltersFromFields", () => {
"it": "",
},
},
"version": "5.2.0",
"version": "5.3.0",
}
`);
});
Expand Down
109 changes: 109 additions & 0 deletions app/configurator/table/configurator/link-base-url-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { sanitizeUrl } from "@braintree/sanitize-url";
import { t } from "@lingui/macro";
import { KeyboardEvent, useEffect, useState } from "react";

import { Input } from "@/components/form";
import { getFieldLabel } from "@/configurator/components/field-i18n";
import {
isConfiguring,
useConfiguratorState,
} from "@/configurator/configurator-state";
import { useLocale } from "@/locales/use-locale";
import { useEvent } from "@/utils/use-event";

export const LinkBaseUrlInput = ({
value,
disabled,
locale,
}: {
value: string;
disabled: boolean;
locale: string;
}) => {
const currentLocale = useLocale();
const [_, dispatch] = useConfiguratorState(isConfiguring);

const [inputValue, setInputValue] = useState(value);
const [isValid, setIsValid] = useState(true);

useEffect(() => {
setInputValue(value);
}, [value]);

const updateBaseUrl = useEvent((newValue: string) => {
dispatch({
type: "CHART_FIELD_UPDATED",
value: {
locale: currentLocale,
field: null,
path: `links.baseUrl.${locale}`,
value: newValue,
},
});
});

const handleCommit = useEvent(() => {
if (inputValue === "") {
setIsValid(true);
updateBaseUrl("");

return;
}

const sanitizedUrl = sanitizeUrl(inputValue);

if (sanitizedUrl === "about:blank") {
setIsValid(false);
updateBaseUrl("");

return;
}

try {
const url = new URL(sanitizedUrl);
const normalizedUrl = normalizeUrl(url);

updateBaseUrl(normalizedUrl);
setIsValid(true);
setInputValue(normalizedUrl);
} catch {
setIsValid(false);
}
});

const handleKeyDown = useEvent((e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
handleCommit();
}
});

return (
<Input
type="url"
label={`${t({
id: "controls.tableSettings.baseUrl",
message: "Base URL",
})} (${getFieldLabel(locale)})`}
name={`links.baseUrl.${locale}`}
placeholder="https://example.com/"
value={inputValue}
disabled={disabled}
error={!isValid}
errorMessage={t({
id: "controls.tableSettings.baseUrlInvalid",
message: "Please enter a valid URL",
})}
onChange={(e) => setInputValue(e.target.value)}
onBlur={handleCommit}
onKeyDown={handleKeyDown}
/>
);
};

const normalizeUrl = (url: URL) => {
if (!url.pathname.endsWith("/")) {
url.pathname = url.pathname + "/";
}

return url.toString();
};
118 changes: 15 additions & 103 deletions app/configurator/table/configurator/links-section.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { sanitizeUrl } from "@braintree/sanitize-url";
import { t, Trans } from "@lingui/macro";
import { SelectChangeEvent } from "@mui/material";
import { KeyboardEvent, useEffect, useMemo, useState } from "react";
import { useMemo } from "react";

import { Input, Select } from "@/components/form";
import { Select } from "@/components/form";
import { TableConfig } from "@/config-types";
import {
ControlSection,
Expand All @@ -16,9 +15,11 @@ import {
useConfiguratorState,
} from "@/configurator/configurator-state";
import { Dimension } from "@/domain/data";
import { useLocale } from "@/locales/use-locale";
import { useLocale, useOrderedLocales } from "@/locales/use-locale";
import { useEvent } from "@/utils/use-event";

import { LinkBaseUrlInput } from "./link-base-url-input";

export const TableLinksSection = ({
chartConfig,
dimensions,
Expand All @@ -27,6 +28,7 @@ export const TableLinksSection = ({
dimensions: Dimension[];
}) => {
const locale = useLocale();
const orderedLocales = useOrderedLocales();
const [_, dispatch] = useConfiguratorState(isConfiguring);

const dimensionOptions = useMemo(() => {
Expand Down Expand Up @@ -92,10 +94,15 @@ export const TableLinksSection = ({
field={null}
path="links.enabled"
/>
<BaseUrlInput
value={chartConfig.links.baseUrl}
disabled={!chartConfig.links.enabled}
/>
{orderedLocales.map((locale) => (
<div key={`${locale}-table-link`}>
<LinkBaseUrlInput
value={chartConfig.links.baseUrl[locale]}
disabled={!chartConfig.links.enabled}
locale={locale}
/>
</div>
))}
<Select
id="links.componentId"
size="sm"
Expand Down Expand Up @@ -126,98 +133,3 @@ export const TableLinksSection = ({
</ControlSection>
);
};

const BaseUrlInput = ({
value,
disabled,
}: {
value: string;
disabled: boolean;
}) => {
const locale = useLocale();
const [_, dispatch] = useConfiguratorState(isConfiguring);

const [inputValue, setInputValue] = useState(value);
const [isValid, setIsValid] = useState(true);

useEffect(() => {
setInputValue(value);
}, [value]);

const updateBaseUrl = useEvent((newValue: string) => {
dispatch({
type: "CHART_FIELD_UPDATED",
value: {
locale,
field: null,
path: "links.baseUrl",
value: newValue,
},
});
});

const handleCommit = useEvent(() => {
if (inputValue === "") {
setIsValid(true);
updateBaseUrl("");

return;
}

const sanitizedUrl = sanitizeUrl(inputValue);

if (sanitizedUrl === "about:blank") {
setIsValid(false);
updateBaseUrl("");

return;
}

try {
const url = new URL(sanitizedUrl);
const normalizedUrl = normalizeUrl(url);

updateBaseUrl(normalizedUrl);
setIsValid(true);
setInputValue(normalizedUrl);
} catch {
setIsValid(false);
}
});

const handleKeyDown = useEvent((e: KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
handleCommit();
}
});

return (
<Input
type="url"
label={t({
id: "controls.tableSettings.baseUrl",
message: "Base URL",
})}
name="links.baseUrl"
placeholder="https://example.com/"
value={inputValue}
disabled={disabled}
error={!isValid}
errorMessage={t({
id: "controls.tableSettings.baseUrlInvalid",
message: "Please enter a valid URL",
})}
onChange={(e) => setInputValue(e.target.value)}
onBlur={handleCommit}
onKeyDown={handleKeyDown}
/>
);
};

const normalizeUrl = (url: URL) => {
if (!url.pathname.endsWith("/")) {
url.pathname = url.pathname + "/";
}

return url.toString();
};
2 changes: 1 addition & 1 deletion app/docs/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1145,7 +1145,7 @@ export const tableConfig: TableConfig = {
settings: { showSearch: true, showAllRows: true, limitColumnWidths: false },
links: {
enabled: false,
baseUrl: "",
baseUrl: { de: "", fr: "", it: "", en: "" },
componentId: "",
targetComponentId: "",
},
Expand Down
4 changes: 2 additions & 2 deletions app/utils/chart-config/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export const CONFIGURATOR_STATE_VERSION = "5.2.0";
export const CONFIGURATOR_STATE_VERSION = "5.3.0";

export const CHART_CONFIG_VERSION = "5.2.0";
export const CHART_CONFIG_VERSION = "5.3.0";
Loading
Loading