diff --git a/.gitignore b/.gitignore index 828d116cc9..3ea33029f8 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,4 @@ storybook-static/ !/e2e_testing/.env e2e_testing/.yarn/cache +.swc diff --git a/RELEASE.rst b/RELEASE.rst index bd37f928c1..f6f6629778 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -1,6 +1,12 @@ Release Notes ============= +Version 0.17.13 +--------------- + +- Better Headings Structure (#1473) +- Fix dialog spacing + reset AddToListDialog (#1484) + Version 0.17.12 (Released August 29, 2024) --------------- diff --git a/frontends/mit-learn/package.json b/frontends/mit-learn/package.json index a0891233a0..236f204e06 100644 --- a/frontends/mit-learn/package.json +++ b/frontends/mit-learn/package.json @@ -29,6 +29,7 @@ "@storybook/react-webpack5": "^8.0.9", "@storybook/test": "^8.0.9", "@swc/core": "^1.4.11", + "@swc/plugin-emotion": "^4.0.0", "@testing-library/react": "14.3.1", "@testing-library/user-event": "14.5.2", "@types/lodash": "^4.14.182", diff --git a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx b/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx deleted file mode 100644 index 682049ab6c..0000000000 --- a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import CardTemplate from "./CardTemplate" -import { DEFAULT_RESOURCE_IMG } from "ol-utilities" -import { makeImgConfig } from "ol-utilities/test-utils/factories" - -describe("CardTemplate", () => { - it("renders title and cover image", () => { - const title = "Test Title" - render( - , - ) - const heading = screen.getByRole("heading", { name: title }) - expect(heading).toHaveAccessibleName(title) - }) -}) diff --git a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx b/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx deleted file mode 100644 index d88b45050c..0000000000 --- a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import React from "react" -import invariant from "tiny-invariant" -import { - MuiCard, - CardContent, - CardMedia, - styled, - TruncateText, -} from "ol-components" -import { RiDraggable } from "@remixicon/react" -import { - DEFAULT_RESOURCE_IMG, - EmbedlyConfig, - embedlyCroppedImage, -} from "ol-utilities" - -type CardVariant = "column" | "row" | "row-reverse" -type OnActivateCard = () => void -type CardTemplateProps = { - /** - * Whether the course picture and info display as a column or row. - */ - variant: CardVariant - className?: string - handleActivate?: OnActivateCard - extraDetails?: React.ReactNode - imgUrl: string - imgConfig: EmbedlyConfig - title?: string - bodySlot?: React.ReactNode - footerSlot?: React.ReactNode - footerActionSlot?: React.ReactNode - sortable?: boolean - suppressImage?: boolean -} - -const LIGHT_TEXT_COLOR = "#8c8c8c" -const SPACER = 0.75 -const SMALL_FONT_SIZE = 0.75 - -const StyledCard = styled(MuiCard)` - display: flex; - flex-direction: column; - - /* Ensure the resource image borders match card borders */ - .MuiCardMedia-root, - > .MuiCardContent-root { - border-radius: inherit; - } -` - -const Details = styled.div` - /* Make content flexbox so that we can control which child fills remaining space. */ - flex: 1; - display: flex; - flex-direction: column; - - > * { - /* - Flexbox doesn't have collapsing margins, so we need to avoid double spacing. - The column-gap property would be a nicer solution, but it doesn't have the - best browser support yet. - */ - margin-top: ${SPACER / 2}rem; - margin-bottom: ${SPACER / 2}rem; - - &:first-of-type { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - } -` - -const StyledCardContent = styled(CardContent, { - shouldForwardProp: (prop) => prop !== "sortable", -})<{ - variant: CardVariant - sortable: boolean -}>` - display: flex; - flex-direction: ${({ variant }) => variant}; - ${({ variant }) => (variant === "column" ? "flex: 1;" : "")} - ${({ sortable }) => (sortable ? "padding-left: 4px;" : "")} -` - -/* - Last child of ol-lrc-content will take up any extra space (flex: 1) but - with its contents at the bottom of its box. - The default is stretch, we we do not want. -*/ -const FillSpaceContentEnd = styled.div` - flex: 1; - display: flex; - flex-direction: column; - justify-content: flex-end; - align-items: flex-start; -` - -const FooterRow = styled.div` - min-height: 2.5 * ${SMALL_FONT_SIZE}; /* ensure consistent spacing even if no date */ - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 100%; -` - -const EllipsisTitle = styled(TruncateText)({ - fontWeight: "bold", - margin: 0, -}) - -const TitleButton = styled.button` - border: none; - background-color: white; - color: inherit; - display: block; - text-align: left; - padding: 0; - margin: 0; - - &:hover { - text-decoration: underline; - cursor: pointer; - } -` - -const DragHandle = styled.div` - display: flex; - align-items: center; - font-size: 40px; - align-self: stretch; - color: ${LIGHT_TEXT_COLOR}; - border-right: 1px solid ${LIGHT_TEXT_COLOR}; - margin-right: 16px; -` - -const CardMediaImage = styled(CardMedia)<{ - variant: CardVariant - component: string - alt: string -}>` - ${({ variant }) => - variant === "row" - ? "margin-right: 16px;" - : variant === "row-reverse" - ? "margin-left: 16px;" - : ""} -` - -type ImageProps = Pick< - CardTemplateProps, - "imgUrl" | "imgConfig" | "suppressImage" | "variant" -> -const CardImage: React.FC = ({ - imgUrl, - imgConfig, - suppressImage, - variant, -}) => { - if (suppressImage) return null - const dims = - variant === "column" - ? { height: imgConfig.height } - : { width: imgConfig.width, height: imgConfig.height } - - return ( - - ) -} - -const CardTemplate = ({ - variant, - className, - handleActivate, - extraDetails, - imgUrl, - imgConfig, - title, - bodySlot, - footerSlot, - footerActionSlot, - sortable = false, - suppressImage = false, -}: CardTemplateProps) => { - invariant( - !sortable || variant === "row-reverse", - "sortable only supported for variant='row-reverse'", - ) - - const image = ( - - ) - - return ( - - {variant === "column" ? image : null} - - {variant !== "column" ? image : null} -
- {extraDetails} - {handleActivate ? ( - - - {title} - - - ) : ( - - {title} - - )} - {sortable ? null : ( - <> - {bodySlot} - - -
{footerSlot}
- {footerActionSlot} -
-
- - )} -
- {sortable ? ( - - - - ) : null} -
-
- ) -} - -export default CardTemplate -export type { CardTemplateProps } diff --git a/frontends/mit-learn/src/page-components/Dialogs/AddToListDialog.tsx b/frontends/mit-learn/src/page-components/Dialogs/AddToListDialog.tsx index a9642557a7..037a39031e 100644 --- a/frontends/mit-learn/src/page-components/Dialogs/AddToListDialog.tsx +++ b/frontends/mit-learn/src/page-components/Dialogs/AddToListDialog.tsx @@ -1,11 +1,12 @@ -import React, { useCallback, useId } from "react" +import React, { useCallback } from "react" import { - Dialog, LoadingSpinner, Typography, styled, CheckboxChoiceField, Button, + FormDialog, + DialogActions, } from "ol-components" import { RiAddLine } from "@remixicon/react" @@ -33,17 +34,9 @@ const ResourceTitle = styled.span({ fontStyle: "italic", }) -const CheckboxContainer = styled.div({ - padding: "28px 0", -}) - -const ButtonContainer = styled.div({ +const Actions = styled(DialogActions)({ display: "flex", - gap: "12px", - alignItems: "flex-start", - button: { - width: "50%", - }, + "> *": { flex: 1 }, }) type AddToListDialogInnerProps = { @@ -100,7 +93,6 @@ const AddToListDialogInner: React.FC = ({ : null, ) .filter((value) => value !== null) - const formId = useId() const formik = useFormik({ enableReinitialize: true, validateOnChange: false, @@ -130,59 +122,61 @@ const AddToListDialogInner: React.FC = ({ }) return ( - + + + + } > {isReady ? ( -
+ <> Adding {resource?.title} - - {listType === ListType.LearningPath ? ( - - ) : null} - {listType === ListType.UserList ? ( - - ) : null} - - - - - -
+ + {listType === ListType.LearningPath ? ( + + ) : null} + {listType === ListType.UserList ? ( + + ) : null} + ) : ( )} -
+ ) } diff --git a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx index e791c0d4a7..10b770818b 100644 --- a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx +++ b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx @@ -175,11 +175,14 @@ describe("ItemsListing", () => { />, { user: {} }, ) + const titles = items.map((item) => item.resource.title) - const headings = screen.getAllByRole("heading", { - name: (value) => titles.includes(value), + + const role = sortable ? "button" : "link" + const cards = screen.getAllByRole(role, { + name: (value) => titles.some((title) => value.includes(title)), }) - expect(headings.map((h) => h.textContent)).toEqual(titles) + expect(cards.length).toBe(titles.length) if (sortable) { items.forEach(({ resource }) => { @@ -320,23 +323,24 @@ describe.each([ListType.LearningPath, ListType.UserList])( const patchResponse = new ControlledPromise() setMockResponse.patch(patchUrl(listType, active.id), patchResponse) - const titleEls1 = screen.getAllByRole("heading", { - name: (value) => titles.includes(value), + // dnd-kit draggables have role button (+ a roledescription) + const cards1 = screen.getAllByRole("button", { + name: (value) => titles.some((title) => value.includes(title)), }) - expect(titleEls1.map((el) => el.textContent)).toEqual(titles) + expect(cards1.length).toBe(titles.length) act(() => simulateDrag(from, to)) await waitFor(() => { - const titleEls2 = screen.getAllByRole("heading", { - name: (value) => titles.includes(value), + const cards2 = screen.getAllByRole("button", { + name: (value) => titles.some((title) => value.includes(title)), }) - expect(titleEls2).toEqual([ - titleEls1[0], - titleEls1[2], - titleEls1[3], - titleEls1[1], - titleEls1[4], + expect(cards2).toEqual([ + cards1[0], + cards1[2], + cards1[3], + cards1[1], + cards1[4], ]) }) }) diff --git a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx index 13627b7bf0..cb8640bd1b 100644 --- a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx +++ b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx @@ -26,13 +26,12 @@ type ItemsListingComponentProps = { condensed?: boolean } -const HeaderText = styled.div(({ theme }) => ({ - h3: { - ...theme.typography.h3, - [theme.breakpoints.down("sm")]: { - marginBottom: "24px", - ...theme.typography.h5, - }, +const HeaderText = styled.h1(({ theme }) => ({ + margin: 0, + ...theme.typography.h3, + [theme.breakpoints.down("sm")]: { + marginBottom: "24px", + ...theme.typography.h5, }, })) @@ -110,9 +109,7 @@ const ItemsListingComponent: React.FC = ({ marginBottom="24px" > - - {list?.title} - + {list?.title} {list?.description && ( {list.description} )} diff --git a/frontends/mit-learn/src/page-components/ManageListDialogs/ManageListDialogs.tsx b/frontends/mit-learn/src/page-components/ManageListDialogs/ManageListDialogs.tsx index cb221be6f7..7607f6d67c 100644 --- a/frontends/mit-learn/src/page-components/ManageListDialogs/ManageListDialogs.tsx +++ b/frontends/mit-learn/src/page-components/ManageListDialogs/ManageListDialogs.tsx @@ -122,14 +122,6 @@ const UpsertLearningPathDialog = NiceModal.create( onSubmit={formik.handleSubmit} confirmText="Save" noValidate - footerContent={ - mutation.isError && - !formik.isSubmitting && ( - - There was a problem saving your list. Please try again later. - - ) - } > formik.setFieldValue(e.name, e.value)} /> + {mutation.isError && !formik.isSubmitting && ( + + There was a problem saving your list. Please try again later. + + )} ) }, @@ -242,14 +239,6 @@ const UpsertUserListDialog = NiceModal.create( onSubmit={formik.handleSubmit} confirmText={userList ? "Update" : "Create"} noValidate - footerContent={ - mutation.isError && - !formik.isSubmitting && ( - - There was a problem saving your list. Please try again later. - - ) - } > + {mutation.isError && !formik.isSubmitting && ( + + There was a problem saving your list. Please try again later. + + )} {userList && (
@@ -148,11 +153,11 @@ const Dialog: React.FC = ({ > {confirmText} - + )} ) } -export { Dialog } +export { Dialog, DialogActions } export type { DialogProps } diff --git a/frontends/ol-components/src/components/FormDialog/FormDialog.stories.tsx b/frontends/ol-components/src/components/FormDialog/FormDialog.stories.tsx index fbe2a9336b..1c0f3c5ad3 100644 --- a/frontends/ol-components/src/components/FormDialog/FormDialog.stories.tsx +++ b/frontends/ol-components/src/components/FormDialog/FormDialog.stories.tsx @@ -72,6 +72,5 @@ export const Simple: Story = { args: { title: "Form Title", fullWidth: true, - footerContent: "Footer content", }, } diff --git a/frontends/ol-components/src/components/FormDialog/FormDialog.tsx b/frontends/ol-components/src/components/FormDialog/FormDialog.tsx index 7b4ec1e616..85d7cbb281 100644 --- a/frontends/ol-components/src/components/FormDialog/FormDialog.tsx +++ b/frontends/ol-components/src/components/FormDialog/FormDialog.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react" import styled from "@emotion/styled" import { Dialog } from "../Dialog/Dialog" -import type { DialogProps } from "@mui/material/Dialog" +import type { DialogProps } from "../Dialog/Dialog" const FormContent = styled.div` display: flex; @@ -50,20 +50,12 @@ interface FormDialogProps { * The form content. These will be direct children of MUI's [DialogContent](https://mui.com/material-ui/api/dialog-content/) */ children?: React.ReactNode - /** - * Extra content below the cancel/submit buttons. This is useful, e.g., for - * displaying overall form error messages. - */ - footerContent?: React.ReactNode + actions?: DialogProps["actions"] /** * Class applied to the `
` element. */ formClassName?: string - /** - * MUI Dialog's [TransitionProps](https://mui.com/material-ui/api/dialog/#props) - */ - TransitionProps?: DialogProps["TransitionProps"] /** * If `true`, the dialog stretches to its `maxWidth`. * @@ -93,7 +85,7 @@ const FormDialog: React.FC = ({ title, noValidate, children, - footerContent, + actions, confirmText = "Submit", cancelText = "Cancel", className, @@ -139,11 +131,9 @@ const FormDialog: React.FC = ({ isSubmitting={isSubmitting} className={className} PaperProps={paperProps} + actions={actions} > - - {children} - {footerContent} - + {children} ) } diff --git a/frontends/ol-components/src/components/FormHelpers/FormHelpers.tsx b/frontends/ol-components/src/components/FormHelpers/FormHelpers.tsx index 65cc5f2c95..1e67cf3ccf 100644 --- a/frontends/ol-components/src/components/FormHelpers/FormHelpers.tsx +++ b/frontends/ol-components/src/components/FormHelpers/FormHelpers.tsx @@ -35,7 +35,7 @@ const Container = styled.div<{ fullWidth?: boolean }>(({ fullWidth }) => [ display: "inline-flex", flexDirection: "column", alignItems: "start", - "> *": { + "> *:not(:last-child)": { marginBottom: "4px", }, }, diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx index 8660f1807a..a059281b20 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx @@ -1,6 +1,6 @@ import React from "react" import { BrowserRouter } from "react-router-dom" -import { screen, render, act } from "@testing-library/react" +import { screen, render } from "@testing-library/react" import { LearningResourceCard } from "./LearningResourceCard" import type { LearningResourceCardProps } from "./LearningResourceCard" import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities" @@ -11,10 +11,7 @@ import { ThemeProvider } from "../ThemeProvider/ThemeProvider" const setup = (props: LearningResourceCardProps) => { return render( - + , { wrapper: ThemeProvider }, ) @@ -30,7 +27,7 @@ describe("Learning Resource Card", () => { setup({ resource }) screen.getByText("Course") - screen.getByRole("heading", { name: resource.title }) + screen.getByText(resource.title) screen.getByText("Starts:") screen.getByText("January 01, 2026") }) @@ -94,20 +91,18 @@ describe("Learning Resource Card", () => { }, ) - test("Click to navigate", async () => { + test("Links to specified href", async () => { const resource = factories.learningResources.resource({ resource_type: ResourceTypeEnum.Course, platform: { code: PlatformEnum.Ocw }, }) - setup({ resource }) + setup({ resource, href: "/path/to/thing" }) - const heading = screen.getByRole("heading", { name: resource.title }) - await act(async () => { - await heading.click() + const link = screen.getByRole("link", { + name: new RegExp(resource.title), }) - - expect(window.location.search).toBe(`?resource=${resource.id}`) + expect(new URL(link.href).pathname).toBe("/path/to/thing") }) test("Click action buttons", async () => { diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx index df930f2f53..9189cb40d1 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx @@ -1,6 +1,6 @@ import React from "react" import { BrowserRouter } from "react-router-dom" -import { screen, render, act } from "@testing-library/react" +import { screen, render } from "@testing-library/react" import { LearningResourceListCard } from "./LearningResourceListCard" import type { LearningResourceListCardProps } from "./LearningResourceListCard" import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities" @@ -11,10 +11,7 @@ import { ThemeProvider } from "../ThemeProvider/ThemeProvider" const setup = (props: LearningResourceListCardProps) => { return render( - + , { wrapper: ThemeProvider }, ) @@ -30,7 +27,7 @@ describe("Learning Resource List Card", () => { setup({ resource }) screen.getByText("Course") - screen.getByRole("heading", { name: resource.title }) + screen.getByText(resource.title) screen.getByText("Starts:") screen.getByText("January 01, 2026") }) @@ -92,14 +89,13 @@ describe("Learning Resource List Card", () => { platform: { code: PlatformEnum.Ocw }, }) - setup({ resource }) + setup({ resource, href: "/path/to/thing" }) - const heading = screen.getByRole("heading", { name: resource.title }) - await act(async () => { - await heading.click() + const card = screen.getByRole("link", { + name: new RegExp(resource.title), }) - expect(window.location.search).toBe(`?resource=${resource.id}`) + expect(card).toHaveAttribute("href", "/path/to/thing") }) test("Click action buttons", async () => { diff --git a/frontends/ol-components/src/components/ThemeProvider/typography.ts b/frontends/ol-components/src/components/ThemeProvider/typography.ts index 22c6f575d0..c358c86857 100644 --- a/frontends/ol-components/src/components/ThemeProvider/typography.ts +++ b/frontends/ol-components/src/components/ThemeProvider/typography.ts @@ -147,6 +147,11 @@ const globalSettings: ThemeOptions["typography"] = { const component: NonNullable["MuiTypography"] = { defaultProps: { variantMapping: { + h1: "span", + h2: "span", + h3: "span", + h4: "span", + h5: "span", body1: "p", body2: "p", body3: "p", diff --git a/frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx b/frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx new file mode 100644 index 0000000000..c23456ea7f --- /dev/null +++ b/frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx @@ -0,0 +1,30 @@ +import styled from "@emotion/styled" + +/** + * VisuallyHidden is a utility component that hides its children from sighted + * users, but keeps them accessible to screen readers. + * + * Often, screenreader-only content can be handled with an `aria-label`. However, + * occasionally we need actual elements. + * + * Example: + * - a visually hidden Heading for a section whose purpose is clear for sighted users + * - a visually hidden description used for aria-describeddby + * - There is an aria-descriptionby attribute that can be used to provide a + * without an actual element on the page. However, it is introduced in + * ARIA 1.3 (working draft), not compatible with some screen readers, and + * flagged problematic by our linting. + * + * The CSS here is based on https://inclusive-components.design/tooltips-toggletips/ + */ +const VisuallyHidden = styled.span({ + clipPath: "inset(100%)", + clip: "rect(1px, 1px, 1px, 1px)", + height: "1px", + overflow: "hidden", + position: "absolute", + whiteSpace: "nowrap", + width: "1px", +}) + +export { VisuallyHidden } diff --git a/frontends/ol-components/src/index.ts b/frontends/ol-components/src/index.ts index 7ee530994a..7c04f30eec 100644 --- a/frontends/ol-components/src/index.ts +++ b/frontends/ol-components/src/index.ts @@ -64,11 +64,11 @@ export type { ContainerProps } from "@mui/material/Container" export { default as MuiDialog } from "@mui/material/Dialog" export type { DialogProps as MuiDialogProps } from "@mui/material/Dialog" -export { default as DialogActions } from "@mui/material/DialogActions" +export { default as MuiDialogActions } from "@mui/material/DialogActions" export type { DialogActionsProps } from "@mui/material/DialogActions" -export { default as DialogContent } from "@mui/material/DialogContent" +export { default as MuiDialogContent } from "@mui/material/DialogContent" export type { DialogContentProps } from "@mui/material/DialogContent" -export { default as DialogTitle } from "@mui/material/DialogTitle" +export { default as MuiDialogTitle } from "@mui/material/DialogTitle" export type { DialogTitleProps } from "@mui/material/DialogTitle" export { default as Divider } from "@mui/material/Divider" @@ -193,6 +193,7 @@ export * from "./components/ThemeProvider/ThemeProvider" export * from "./components/TruncateText/TruncateText" export * from "./components/Radio/Radio" export * from "./components/RadioChoiceField/RadioChoiceField" +export * from "./components/VisuallyHidden/VisuallyHidden" export * from "./constants/imgConfigs" diff --git a/frontends/ol-test-utilities/package.json b/frontends/ol-test-utilities/package.json index d1fbac79e1..912aa20650 100644 --- a/frontends/ol-test-utilities/package.json +++ b/frontends/ol-test-utilities/package.json @@ -10,6 +10,7 @@ "@faker-js/faker": "^8.0.0", "@testing-library/react": "16.0.0", "css-mediaquery": "^0.1.2", + "dom-accessibility-api": "^0.7.0", "tiny-invariant": "^1.3.1" } } diff --git a/frontends/ol-test-utilities/src/assertions.ts b/frontends/ol-test-utilities/src/assertions.ts new file mode 100644 index 0000000000..7128ec0895 --- /dev/null +++ b/frontends/ol-test-utilities/src/assertions.ts @@ -0,0 +1,26 @@ +import { screen } from "@testing-library/react" +/** + * This is the library that @testing-library uses to compute accessible names. + */ +import { computeAccessibleName } from "dom-accessibility-api" + +type HeadingSpec = { + level: number + /** + * The accessible name of the heading. + * Can be a matcher like `expect.stringContaining("foo")`. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + name: any +} +const assertHeadings = (expected: HeadingSpec[]) => { + const headings = screen.getAllByRole("heading") + const actual = headings.map((heading) => { + const level = parseInt(heading.tagName[1], 10) + const name = computeAccessibleName(heading) + return { level, name } + }) + expect(actual).toEqual(expected) +} + +export { assertHeadings } diff --git a/frontends/ol-test-utilities/src/index.ts b/frontends/ol-test-utilities/src/index.ts index 5bd11487e9..ae10ad5a02 100644 --- a/frontends/ol-test-utilities/src/index.ts +++ b/frontends/ol-test-utilities/src/index.ts @@ -2,3 +2,4 @@ export { default as ControlledPromise } from "./ControlledPromise/ControlledProm export * from "./factories" export * from "./domQueries" export * from "./mocks/mocks" +export * from "./assertions" diff --git a/frontends/ol-widgets/src/components/editing/ManageWidgetDialog.tsx b/frontends/ol-widgets/src/components/editing/ManageWidgetDialog.tsx index 2240a698e0..3e1c0d97a9 100644 --- a/frontends/ol-widgets/src/components/editing/ManageWidgetDialog.tsx +++ b/frontends/ol-widgets/src/components/editing/ManageWidgetDialog.tsx @@ -1,9 +1,9 @@ import React, { useId, useCallback, useEffect, useMemo, useState } from "react" import { - Dialog, - DialogActions, - DialogContent, - DialogTitle, + MuiDialog, + MuiDialogActions, + MuiDialogContent, + MuiDialogTitle, Button, RadioChoiceField, } from "ol-components" @@ -122,7 +122,7 @@ const DialogContentEditing: React.FC = ({ ) return ( <> - {title} + {title} >} validationSchema={validationSchema} @@ -135,7 +135,7 @@ const DialogContentEditing: React.FC = ({ const { htmlFor: labelFor, ...labelAttrs } = titleAttrs.label return ( - +
) })} -
- + + - + ) }} @@ -275,11 +275,11 @@ const DialogContentAdding: React.FC = ({ ) return ( <> - New widget + New widget {({ handleSubmit, values }) => (
- + = ({ /> )} - - + + - +
)}
@@ -345,7 +345,7 @@ const ManageWidgetDialog: React.FC = ({ [], ) return ( - = ({ classes={classes} /> )} - + ) } diff --git a/main/settings.py b/main/settings.py index cf48dc1a9a..8c66f7f483 100644 --- a/main/settings.py +++ b/main/settings.py @@ -33,7 +33,7 @@ from main.settings_pluggy import * # noqa: F403 from openapi.settings_spectacular import open_spectacular_settings -VERSION = "0.17.12" +VERSION = "0.17.13" log = logging.getLogger() diff --git a/yarn.lock b/yarn.lock index 13500e370a..60766f3154 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4648,6 +4648,15 @@ __metadata: languageName: node linkType: hard +"@swc/plugin-emotion@npm:^4.0.0": + version: 4.0.0 + resolution: "@swc/plugin-emotion@npm:4.0.0" + dependencies: + "@swc/counter": "npm:^0.1.3" + checksum: 10/31159f1a510732f48b9e3bdd9929f6f503bb866957d15ebcc8635f273b1aebad23f23b6798e6c0d426d5a015b15403b20fcd3102ac33c7908e614ab480bf92e3 + languageName: node + linkType: hard + "@swc/types@npm:^0.1.12": version: 0.1.12 resolution: "@swc/types@npm:0.1.12" @@ -8782,6 +8791,13 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.7.0": + version: 0.7.0 + resolution: "dom-accessibility-api@npm:0.7.0" + checksum: 10/ac69d27099bc0650633545c461347a355c2794b330f69b6b67fd98df91be9a48547d85176758747841e10ee041905143b39b6094e72c704c265b0f4f9bb7ab28 + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" @@ -14769,6 +14785,7 @@ __metadata: "@storybook/react-webpack5": "npm:^8.0.9" "@storybook/test": "npm:^8.0.9" "@swc/core": "npm:^1.4.11" + "@swc/plugin-emotion": "npm:^4.0.0" "@tanstack/react-query": "npm:^4.36.1" "@tanstack/react-query-devtools": "npm:^4.29.6" "@testing-library/react": "npm:14.3.1" @@ -15413,6 +15430,7 @@ __metadata: "@faker-js/faker": "npm:^8.0.0" "@testing-library/react": "npm:16.0.0" css-mediaquery: "npm:^0.1.2" + dom-accessibility-api: "npm:^0.7.0" tiny-invariant: "npm:^1.3.1" languageName: unknown linkType: soft