Skip to content

Commit eb14634

Browse files
authored
Merge pull request #65 from feedzai/develop
Code for release 3.0.0
2 parents 7fbf0bd + 8532c80 commit eb14634

35 files changed

Lines changed: 461 additions & 233 deletions

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Implemented i18n alternatives as the new "internationalization" property
1313
- Fixed tests / github pipeline errors
1414
- Adapted the ShortcutGuide to vary between "Alt" and "option" depending on the OS
15+
- Added support for OpenAI API-compatible providers via the new `baseUrl` prop
16+
- Added new i18n Cypress tests
17+
- Added Arrow Navigation to the ShortcutGuide
18+
19+
### Changed
20+
21+
- Replaced arrow symbols with text on the ShortcutGuide
22+
- Re-did ShortcutGuide `<dl>` structure
23+
- Moved the context prop to inside autoDescriptions
1524

1625
## 2.0.4 - 2025-01-06
1726

README.md

Lines changed: 18 additions & 19 deletions
Large diffs are not rendered by default.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* eslint-disable no-undef */
2+
describe("Internationalization Tests", () => {
3+
it("should use default English (en-GB) language when internationalization property is empty", () => {
4+
cy.visit("/");
5+
cy.injectAxe();
6+
cy.findByTestId("manual-descriptions-option").click();
7+
cy.wait(500);
8+
cy.findAllByTestId("a11y_desc").first().focus();
9+
cy.focused().type("{alt}k");
10+
cy.findAllByTestId("a11y-chart-alert").first().should("contain.text", "The average is");
11+
cy.focused().type("?");
12+
cy.get("dialog").should("be.visible");
13+
cy.get("dialog h2").should("contain.text", "Shortcut guide");
14+
cy.focused().type("{esc}");
15+
cy.checkA11y();
16+
});
17+
18+
it("should use default Portuguese (pt-PT) language when language property is pt-PT", () => {
19+
cy.visit("/");
20+
cy.injectAxe();
21+
cy.findByTestId("manual-descriptions-option").click();
22+
cy.wait(500);
23+
cy.findAllByTestId("a11y_desc").first().focus().tab().tab();
24+
cy.focused().type("{alt}k");
25+
cy.findAllByTestId("a11y-chart-alert").eq(2).should("contain.text", "A média é");
26+
cy.focused().type("?");
27+
cy.get("dialog").should("be.visible");
28+
cy.get("dialog h2").should("contain.text", "Guia de atalhos");
29+
cy.checkA11y();
30+
});
31+
32+
it("should use an overriten en-GB string", () => {
33+
cy.visit("/");
34+
cy.injectAxe();
35+
cy.findByTestId("manual-descriptions-option").click();
36+
cy.wait(500);
37+
cy.findAllByTestId("a11y_desc").first().focus().tab().tab();
38+
cy.focused().type("?");
39+
cy.get("dialog").should("be.visible");
40+
cy.get("dialog h2").should("contain.text", "Overwritten Shortcut guide title");
41+
cy.checkA11y();
42+
});
43+
});

examples/src/Homepage.jsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import CardGridManual from "./components/manual/ChartGridManual";
77

88
function Homepage() {
99
const [apiKey, setApiKey] = useState("");
10+
const [model, setModel] = useState("gpt-3.5-turbo");
11+
const [baseUrl, setBaseUrl] = useState("https://api.openai.com/v1/");
1012
const [isValid, setIsValid] = useState(false);
1113
const [automatic, setAutomatic] = useState(false);
1214
const [manual, setManual] = useState(false);
@@ -38,7 +40,7 @@ function Homepage() {
3840
{isValid && apiKey !== "" ? (
3941
<>
4042
<div style={{ marginTop: 20 }}>
41-
<CardGrid apiKey={apiKey} setHome={setHome} />
43+
<CardGrid apiKey={apiKey} model={model} baseUrl={baseUrl} setHome={setHome} />
4244
</div>
4345
<p>
4446
Data from{" "}
@@ -64,6 +66,10 @@ function Homepage() {
6466
<KeyRequest
6567
apiKey={apiKey}
6668
setApiKey={setApiKey}
69+
model={model}
70+
setModel={setModel}
71+
baseUrl={baseUrl}
72+
setBaseUrl={setBaseUrl}
6773
isValid={isValid}
6874
setIsValid={setIsValid}
6975
home={home}
@@ -72,16 +78,7 @@ function Homepage() {
7278
/>
7379
</div>
7480
) : home ? (
75-
<Options
76-
apiKey={apiKey}
77-
setApiKey={setApiKey}
78-
isValid={isValid}
79-
setIsValid={setIsValid}
80-
automatic={automatic}
81-
setAutomatic={setAutomatic}
82-
manual={manual}
83-
setManual={setManual}
84-
/>
81+
<Options setAutomatic={setAutomatic} setManual={setManual} />
8582
) : (
8683
<p></p>
8784
)}

examples/src/KeyRequest.jsx

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,55 @@ import CardContent from "@mui/material/CardContent";
33
import Typography from "@mui/material/Typography";
44
import "./assets/style/Options.css";
55

6-
const KeyRequest = ({ apiKey, setApiKey, setIsValid, setHome }) => {
6+
const KeyRequest = ({
7+
apiKey,
8+
setApiKey,
9+
model,
10+
setModel,
11+
baseUrl,
12+
setBaseUrl,
13+
setIsValid,
14+
setHome,
15+
}) => {
716
setHome(false);
817

9-
const handleChanges = (e) => {
18+
const isValidOpenAiApiKey = (key) => {
19+
return key.startsWith("sk-proj-");
20+
};
21+
22+
const isOpenAiApi = (baseUrl) => {
23+
const parsedBaseUrl = URL.parse(baseUrl);
24+
25+
if (parsedBaseUrl) {
26+
return parsedBaseUrl.host === "api.openai.com";
27+
}
28+
29+
return false;
30+
};
31+
32+
const handleApiKeyChange = (e) => {
1033
setApiKey(e.target.value);
1134
};
1235

36+
const handleModelChange = (e) => {
37+
setModel(e.target.value);
38+
};
39+
40+
const handleBaseUrl = (e) => {
41+
setBaseUrl(e.target.value);
42+
};
43+
1344
const handleSubmit = (e) => {
1445
e.preventDefault();
15-
if (isValidApiKey(apiKey)) {
16-
setIsValid(true);
17-
} else {
18-
alert(
19-
"The API key should start with 'sk-' and be followed by a string of exactly 48 alphanumeric characters.",
20-
);
46+
47+
if (isOpenAiApi(baseUrl) && !isValidOpenAiApiKey(apiKey)) {
48+
alert("The OpenAI API key should start with 'sk-proj-'.");
2149
setIsValid(false);
50+
} else {
51+
setIsValid(true);
2252
}
2353
};
2454

25-
const isValidApiKey = (key) => {
26-
const apiKeyPattern = /^sk-(proj-)?[a-zA-Z0-9]{48}$/;
27-
return apiKeyPattern.test(key);
28-
};
29-
3055
return (
3156
<>
3257
<div id="cardContainer">
@@ -38,28 +63,45 @@ const KeyRequest = ({ apiKey, setApiKey, setIsValid, setHome }) => {
3863
<Typography variant="body2" color="text.secondary" style={{ whiteSpace: "pre-wrap" }}>
3964
Please provide an{" "}
4065
<a href="https://platform.openai.com/account/api-keys" target="_blank">
41-
OpenAI API key
66+
OpenAI
4267
</a>{" "}
43-
(the key is not saved). <br></br>The API key should start with 'sk-' and be followed
44-
by a string of exactly 48 alphanumeric characters.
68+
or OpenAI-compatible API key (the key is not saved). <br></br>The OpenAI API key
69+
should start with 'sk-proj-'.
4570
</Typography>
4671
<br />
4772
<label>
48-
API Key:{" "}
73+
API key:{" "}
4974
<input
5075
type="text"
51-
placeholder="sk-... OR sk-proj-..."
76+
required
77+
placeholder="sk-proj-... OR ..."
5278
value={apiKey}
53-
onChange={handleChanges}
79+
onChange={handleApiKeyChange}
5480
/>
5581
</label>
82+
<br />
83+
<label>
84+
Model: <input type="text" required value={model} onChange={handleModelChange} />
85+
</label>
86+
<br />
87+
<label>
88+
Base URL:{" "}
89+
<input
90+
type="url"
91+
required
92+
pattern="^https://.*|^http://localhost:\d+.*"
93+
value={baseUrl}
94+
onChange={handleBaseUrl}
95+
/>
96+
</label>
97+
<br />
5698
<button type="submit" tabIndex={0}>
5799
Submit
58100
</button>
59101
</form>
60-
{apiKey.length > 0 && !isValidApiKey(apiKey) && (
102+
{apiKey.length > 0 && !isValidOpenAiApiKey(apiKey) && isOpenAiApi(baseUrl) && (
61103
<p style={{ color: "red" }} role="alert">
62-
Invalid API key format.
104+
Invalid OpenAI API key format.
63105
</p>
64106
)}
65107
</CardContent>

examples/src/Options.jsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ const Options = ({ setAutomatic, setManual }) => {
2020
<CardContent>
2121
<h2>Option A</h2> <h3>Automatic Descriptions</h3>
2222
<Typography variant="body2" color="text.secondary" style={{ whiteSpace: "pre-wrap" }}>
23-
The descriptions for each chart are generated in the moment using a GPT model.
24-
Because of that, <b> an OpenAI API key is needed.</b>
23+
The descriptions for each chart are generated by an OpenAI model or one accessible
24+
via an OpenAI-compatible API. Because of that,{" "}
25+
<b>an OpenAI or OpenAI-compatible API key is needed.</b>
2526
</Typography>
2627
</CardContent>
2728
</CardActionArea>
@@ -32,8 +33,9 @@ const Options = ({ setAutomatic, setManual }) => {
3233
<h2>Option B</h2>
3334
<h3>Manual Descriptions</h3>
3435
<Typography variant="body2" color="text.secondary" style={{ whiteSpace: "pre-wrap" }}>
35-
The descriptions for each chart were previously generated using a GPT model. They
36-
were then manually added to each chart. <b>No OpenAI API key is needed.</b>
36+
The descriptions for each chart were previously generated using an OpenAI model.
37+
They were then manually added to each chart.{" "}
38+
<b>No OpenAI or OpenAI-compatible API key is needed.</b>
3739
</Typography>
3840
</CardContent>
3941
</CardActionArea>

examples/src/components/automatic/BarChartAutomatic.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { chartDimensions } from "./chart.constants.js";
99
import { AutoVizuA11y } from "@feedzai/autovizua11y";
1010
import transformJSON from "../../data/transformJSON.js";
1111

12-
function BarChart({ apiKey }) {
12+
function BarChart({ apiKey, model, baseUrl }) {
1313
const intValuesCountryData = countryData.map((country) => {
1414
return {
1515
...country, //copies all countrys first...
@@ -110,7 +110,8 @@ function BarChart({ apiKey }) {
110110
autoDescriptions={{
111111
dynamicDescriptions: false,
112112
apiKey: apiKey,
113-
model: "gpt-3.5-turbo",
113+
model: model,
114+
baseUrl: baseUrl,
114115
temperature: 0.1,
115116
}}
116117
>

examples/src/components/automatic/ChartGrid.jsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,64 +10,72 @@ import Heatmap from "./HeatmapAutomatic";
1010
import StackedBar from "./StackedBarAutomatic";
1111
import Scatterplot from "./ScatterplotAutomatic";
1212

13-
function CardGrid({ apiKey, setHome }) {
13+
function CardGrid({ apiKey, model, baseUrl, setHome }) {
1414
setHome(false);
1515
return (
1616
<Grid container direction="row" spacing={2} justifyContent="center" alignItems="center">
1717
<Grid item>
1818
<Card sx={{ minWidth: 275, width: 450 }}>
1919
<CardContent>
20-
<BarChart apiKey={apiKey}></BarChart>
20+
<BarChart apiKey={apiKey} model={model} baseUrl={baseUrl}></BarChart>
2121
</CardContent>
2222
</Card>
2323
</Grid>
2424
<Grid item>
2525
<Card sx={{ minWidth: 275, width: 450 }}>
2626
<CardContent>
27-
<SingleSeriesTimeline apiKey={apiKey}></SingleSeriesTimeline>
27+
<SingleSeriesTimeline
28+
apiKey={apiKey}
29+
model={model}
30+
baseUrl={baseUrl}
31+
></SingleSeriesTimeline>
2832
</CardContent>
2933
</Card>
3034
</Grid>
3135
<Grid item>
3236
<Card sx={{ minWidth: 275, width: 450 }}>
3337
<CardContent>
34-
<MultiSeriesTimeline apiKey={apiKey}></MultiSeriesTimeline>
38+
<MultiSeriesTimeline
39+
apiKey={apiKey}
40+
model={model}
41+
baseUrl={baseUrl}
42+
></MultiSeriesTimeline>
3543
</CardContent>
3644
</Card>
3745
</Grid>
3846

3947
<Grid item>
4048
<Card sx={{ minWidth: 275, width: 450 }}>
4149
<CardContent>
42-
<Pie_ apiKey={apiKey}></Pie_>
50+
<Pie_ apiKey={apiKey} model={model} baseUrl={baseUrl}></Pie_>
4351
</CardContent>
4452
</Card>
4553
</Grid>
4654
<Grid item>
4755
<Card sx={{ minWidth: 275, width: 450 }}>
4856
<CardContent>
49-
<Treemap_ apiKey={apiKey}></Treemap_>
57+
<Treemap_ apiKey={apiKey} model={model} baseUrl={baseUrl}></Treemap_>
5058
</CardContent>
5159
</Card>
5260
</Grid>
5361
<Grid item>
5462
<Card sx={{ minWidth: 275, width: 450 }}>
5563
<CardContent>
56-
<Heatmap apiKey={apiKey}></Heatmap>
64+
<Heatmap apiKey={apiKey} model={model} baseUrl={baseUrl}></Heatmap>
5765
</CardContent>
5866
</Card>
5967
</Grid>
6068
<Grid item>
6169
<Card sx={{ minWidth: 275, width: 450 }}>
6270
<CardContent>
63-
<StackedBar apiKey={apiKey}></StackedBar>
71+
<StackedBar apiKey={apiKey} model={model} baseUrl={baseUrl}></StackedBar>
6472
</CardContent>
6573
</Card>
6674
</Grid>
6775
<Grid item>
6876
<Card sx={{ minWidth: 275, width: 450 }}>
6977
<CardContent>
70-
<Scatterplot apiKey={apiKey}></Scatterplot>
78+
<Scatterplot apiKey={apiKey} model={model} baseUrl={baseUrl}></Scatterplot>
7179
</CardContent>
7280
</Card>
7381
</Grid>

examples/src/components/automatic/HeatmapAutomatic.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Axis } from "@visx/axis";
99
import { AutoVizuA11y } from "@feedzai/autovizua11y";
1010
import transformJSON from "../../data/transformJSON.js";
1111

12-
function Heatmap({ apiKey }) {
12+
function Heatmap({ apiKey, model, baseUrl }) {
1313
const intValuesCountryData = countryData.map((country) => {
1414
return {
1515
...country, //copies all countrys first...
@@ -211,7 +211,8 @@ function Heatmap({ apiKey }) {
211211
autoDescriptions={{
212212
dynamicDescriptions: false,
213213
apiKey: apiKey,
214-
model: "gpt-3.5-turbo",
214+
model: model,
215+
baseUrl: baseUrl,
215216
temperature: 0.1,
216217
}}
217218
>

examples/src/components/automatic/MultiTimelineAutomatic.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { chartDimensions } from "./chart.constants.js";
88
import { AutoVizuA11y } from "@feedzai/autovizua11y";
99
import transformJSONmulti from "../../data/transformJSONmulti.js";
1010

11-
function MultiSeriesTimeline({ apiKey }) {
11+
function MultiSeriesTimeline({ apiKey, model, baseUrl }) {
1212
const intValuesCountryData = countryData.map((country) => {
1313
return {
1414
...country, //copies all countrys first...
@@ -140,7 +140,8 @@ function MultiSeriesTimeline({ apiKey }) {
140140
autoDescriptions={{
141141
dynamicDescriptions: false,
142142
apiKey: apiKey,
143-
model: "gpt-3.5-turbo",
143+
model: model,
144+
baseUrl: baseUrl,
144145
temperature: 0.1,
145146
}}
146147
>

0 commit comments

Comments
 (0)